From a22be78eeeb5a6f458b15f9af3639f2f1062b430 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 6 Jan 2022 16:29:18 +1100 Subject: [PATCH 001/171] implements futures functions and GRPC functions on new branch --- cmd/gctcli/commands.go | 310 ++++++- cmd/gctcli/helpers.go | 17 + cmd/gctcli/main.go | 2 + engine/order_manager.go | 73 +- engine/order_manager_test.go | 180 +++++ engine/order_manager_types.go | 11 +- engine/rpcserver.go | 181 +++++ engine/rpcserver_test.go | 187 ++++- exchanges/asset/asset.go | 10 + exchanges/exchange.go | 28 + exchanges/ftx/ftx.go | 213 ++++- exchanges/ftx/ftx_test.go | 269 ++++++- exchanges/ftx/ftx_types.go | 29 +- exchanges/ftx/ftx_websocket_test.go | 3 +- exchanges/ftx/ftx_wrapper.go | 210 ++++- exchanges/interfaces.go | 7 +- exchanges/order/futures.go | 566 +++++++++++++ exchanges/order/futures_test.go | 667 +++++++++++++++ exchanges/order/futures_types.go | 228 ++++++ exchanges/order/order_types.go | 2 + exchanges/order/orders.go | 14 + gctrpc/rpc.pb.go | 1157 +++++++++++++++++++++------ gctrpc/rpc.pb.gw.go | 166 ++++ gctrpc/rpc.proto | 59 ++ gctrpc/rpc.swagger.json | 223 ++++++ gctrpc/rpc_grpc.pb.go | 72 ++ 26 files changed, 4530 insertions(+), 354 deletions(-) create mode 100644 exchanges/order/futures.go create mode 100644 exchanges/order/futures_test.go create mode 100644 exchanges/order/futures_types.go diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 50b850370fa..eef898fad87 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -17,7 +17,7 @@ import ( "github.com/urfave/cli/v2" ) -var startTime, endTime, order string +var startTime, endTime, o string var limit int var getInfoCommand = &cli.Command{ @@ -3742,7 +3742,7 @@ var getAuditEventCommand = &cli.Command{ Aliases: []string{"o"}, Usage: "order results by ascending/descending", Value: "asc", - Destination: &order, + Destination: &o, }, &cli.IntFlag{ Name: "limit", @@ -3769,7 +3769,7 @@ func getAuditEvent(c *cli.Context) error { if !c.IsSet("order") { if c.Args().Get(2) != "" { - order = c.Args().Get(2) + o = c.Args().Get(2) } } @@ -3810,7 +3810,7 @@ func getAuditEvent(c *cli.Context) error { StartDate: negateLocalOffset(s), EndDate: negateLocalOffset(e), Limit: int32(limit), - OrderBy: order, + OrderBy: o, }) if err != nil { @@ -4723,17 +4723,293 @@ func findMissingSavedCandleIntervals(c *cli.Context) error { return nil } -// negateLocalOffset helps negate the offset of time generation -// when the unix time gets to rpcserver, it no longer is the same time -// that was sent as it handles it as a UTC value, even though when -// using starttime it is generated as your local time -// eg 2020-01-01 12:00:00 +10 will convert into -// 2020-01-01 12:00:00 +00 when at RPCServer -// so this function will minus the offset from the local sent time -// to allow for proper use at RPCServer -func negateLocalOffset(t time.Time) string { - _, offset := time.Now().Zone() - loc := time.FixedZone("", -offset) - - return t.In(loc).Format(common.SimpleTimeFormat) +var getFuturesPositionsCommand = &cli.Command{ + Name: "getfuturesposition", + Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened", + ArgsUsage: " ", + Action: getFuturesPositions, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "exchange", + Aliases: []string{"e"}, + Usage: "the exchange to retrieve futures positions from", + }, + &cli.StringFlag{ + Name: "asset", + Aliases: []string{"a"}, + Usage: "the asset type of the currency pair, must be a futures type", + }, + &cli.StringFlag{ + Name: "pair", + Aliases: []string{"p"}, + Usage: "the currency pair", + }, + &cli.StringFlag{ + Name: "start", + Aliases: []string{"sd"}, + Usage: " rounded down to the nearest hour, ensure your starting position is within this window for accurate calculations", + Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat), + Destination: &startTime, + }, + &cli.StringFlag{ + Name: "end", + Aliases: []string{"ed"}, + Usage: " rounded down to the nearest hour, ensure your last position is within this window for accurate calculations", + Value: time.Now().Truncate(time.Hour).Format(common.SimpleTimeFormat), + Destination: &endTime, + }, + &cli.IntFlag{ + Name: "limit", + Aliases: []string{"l"}, + Usage: "the number of positions (not orders) to return", + Value: 86400, + Destination: &limit, + }, + &cli.StringFlag{ + Name: "status", + Aliases: []string{"s"}, + Usage: "limit return to position statuses - open, closed, any", + Value: "ANY", + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "includes all orders that make up a position in the response", + }, + }, +} + +func getFuturesPositions(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, "getfuturesposition") + } + + var exchangeName string + if c.IsSet("exchange") { + exchangeName = c.String("exchange") + } else { + exchangeName = c.Args().First() + } + + var assetType string + if c.IsSet("asset") { + assetType = c.String("asset") + } else { + assetType = c.Args().Get(1) + } + + if !validAsset(assetType) { + return errInvalidAsset + } + + var currencyPair string + if c.IsSet("pair") { + currencyPair = c.String("pair") + } else { + currencyPair = c.Args().Get(2) + } + if !validPair(currencyPair) { + return errInvalidPair + } + + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + + if !c.IsSet("start") { + if c.Args().Get(3) != "" { + startTime = c.Args().Get(3) + } + } + + if !c.IsSet("end") { + if c.Args().Get(4) != "" { + endTime = c.Args().Get(4) + } + } + if c.IsSet("limit") { + limit = c.Int("limit") + } else if c.Args().Get(5) != "" { + var limit64 int64 + limit64, err = strconv.ParseInt(c.Args().Get(5), 10, 64) + if err != nil { + return err + } + limit = int(limit64) + } + + var status string + if c.IsSet("status") { + status = c.String("status") + } else if c.Args().Get(6) != "" { + status = c.Args().Get(6) + } + if !strings.EqualFold(status, "any") && + !strings.EqualFold(status, "open") && + !strings.EqualFold(status, "closed") && + status != "" { + return errors.New("unrecognised status") + } + + var verbose bool + if c.IsSet("verbose") { + verbose = c.Bool("verbose") + } else if c.Args().Get(6) != "" { + verbose, err = strconv.ParseBool(c.Args().Get(7)) + if err != nil { + return err + } + } + + var s, e time.Time + s, err = time.Parse(common.SimpleTimeFormat, startTime) + if err != nil { + return fmt.Errorf("invalid time format for start: %v", err) + } + e, err = time.Parse(common.SimpleTimeFormat, endTime) + if err != nil { + return fmt.Errorf("invalid time format for end: %v", err) + } + + if e.Before(s) { + return errors.New("start cannot be after end") + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.GetFuturesPositions(c.Context, + &gctrpc.GetFuturesPositionsRequest{ + Exchange: exchangeName, + Asset: assetType, + Pair: &gctrpc.CurrencyPair{ + Delimiter: p.Delimiter, + Base: p.Base.String(), + Quote: p.Quote.String(), + }, + StartDate: negateLocalOffset(s), + EndDate: negateLocalOffset(e), + Status: status, + PositionLimit: int64(limit), + Verbose: verbose, + }) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var getCollateralCommand = &cli.Command{ + Name: "getcollateral", + Usage: "returns total collateral for an exchange asset, with optional per currency breakdown", + ArgsUsage: " ", + Action: getCollateral, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "exchange", + Aliases: []string{"e"}, + Usage: "the exchange to retrieve futures positions from", + }, + &cli.StringFlag{ + Name: "asset", + Aliases: []string{"a"}, + Usage: "the asset type of the currency pair, must be a futures type", + }, + &cli.BoolFlag{ + Name: "calculateoffline", + Aliases: []string{"c"}, + Usage: "use local scaling methods instead of requesting additional API information, depending on individual exchange support", + }, + &cli.BoolFlag{ + Name: "includebreakdown", + Aliases: []string{"i"}, + Usage: "include a list of each helds currency and its contribution to the overall collateral value", + }, + &cli.StringFlag{ + Name: "subaccount", + Aliases: []string{"s"}, + Usage: "the subaccount to retreieve collateral data from, depending on individual exchange support", + }, + }, +} + +func getCollateral(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, c.Command.Name) + } + + var exchangeName string + if c.IsSet("exchange") { + exchangeName = c.String("exchange") + } else { + exchangeName = c.Args().First() + } + + var assetType string + if c.IsSet("asset") { + assetType = c.String("asset") + } else { + assetType = c.Args().Get(1) + } + + if !validAsset(assetType) { + return errInvalidAsset + } + + var err error + var calculateOffline bool + if c.IsSet("calculateoffline") { + calculateOffline = c.Bool("calculateoffline") + } else if c.Args().Get(2) != "" { + calculateOffline, err = strconv.ParseBool(c.Args().Get(2)) + if err != nil { + return err + } + } + + var includeBreakdown bool + if c.IsSet("includebreakdown") { + includeBreakdown = c.Bool("includebreakdown") + } else if c.Args().Get(3) != "" { + includeBreakdown, err = strconv.ParseBool(c.Args().Get(3)) + if err != nil { + return err + } + } + + var subAccount string + if c.IsSet("subaccount") { + subAccount = c.String("subaccount") + } else if c.Args().Get(4) != "" { + subAccount = c.Args().Get(4) + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.GetCollateral(c.Context, + &gctrpc.GetCollateralRequest{ + Exchange: exchangeName, + Asset: assetType, + SubAccount: subAccount, + IncludeBreakdown: includeBreakdown, + CalculateOffline: calculateOffline, + }) + if err != nil { + return err + } + + jsonOutput(result) + return nil } diff --git a/cmd/gctcli/helpers.go b/cmd/gctcli/helpers.go index 92bc2d0160a..dfe4567e5cb 100644 --- a/cmd/gctcli/helpers.go +++ b/cmd/gctcli/helpers.go @@ -6,7 +6,9 @@ import ( "os" "os/exec" "runtime" + "time" + "github.com/thrasher-corp/gocryptotrader/common" "google.golang.org/grpc" ) @@ -31,3 +33,18 @@ func closeConn(conn *grpc.ClientConn, cancel context.CancelFunc) { cancel() } } + +// negateLocalOffset helps negate the offset of time generation +// when the unix time gets to rpcserver, it no longer is the same time +// that was sent as it handles it as a UTC value, even though when +// using starttime it is generated as your local time +// eg 2020-01-01 12:00:00 +10 will convert into +// 2020-01-01 12:00:00 +00 when at RPCServer +// so this function will minus the offset from the local sent time +// to allow for proper use at RPCServer +func negateLocalOffset(t time.Time) string { + _, offset := time.Now().Zone() + loc := time.FixedZone("", -offset) + + return t.In(loc).Format(common.SimpleTimeFormat) +} diff --git a/cmd/gctcli/main.go b/cmd/gctcli/main.go index b76f468ea62..65ad498f86f 100644 --- a/cmd/gctcli/main.go +++ b/cmd/gctcli/main.go @@ -163,6 +163,8 @@ func main() { tradeCommand, dataHistoryCommands, currencyStateManagementCommand, + getFuturesPositionsCommand, + getCollateralCommand, } ctx, cancel := context.WithCancel(context.Background()) diff --git a/engine/order_manager.go b/engine/order_manager.go index 9a5c60be89d..9a400d0eedc 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -34,10 +34,11 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i return &OrderManager{ shutdown: make(chan struct{}), orderStore: store{ - Orders: make(map[string][]*order.Detail), - exchangeManager: exchangeManager, - commsManager: communicationsManager, - wg: wg, + Orders: make(map[string][]*order.Detail), + exchangeManager: exchangeManager, + commsManager: communicationsManager, + wg: wg, + futuresPositionController: order.SetupPositionController(), }, verbose: verbose, }, nil @@ -224,6 +225,27 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { return nil } +var errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") + +// GetFuturesPositionsForExchange returns futures positions stored within +// the order manager's futures position tracker that match the provided params +func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*order.PositionTracker, error) { + if m == nil { + return nil, fmt.Errorf("order manager %w", ErrNilSubsystem) + } + if atomic.LoadInt32(&m.started) == 0 { + return nil, fmt.Errorf("order manager %w", ErrSubSystemNotStarted) + } + if m.orderStore.futuresPositionController == nil { + return nil, errFuturesTrackerNotSetup + } + if !item.IsFutures() { + return nil, fmt.Errorf("%v %w", item, order.ErrNotFutureAsset) + } + + return m.orderStore.futuresPositionController.GetPositionsForExchange(exch, item, pair) +} + // GetOrderInfo calls the exchange's wrapper GetOrderInfo function // and stores the result in the order manager func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID string, cp currency.Pair, a asset.Item) (order.Detail, error) { @@ -258,11 +280,11 @@ func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID s // validate ensures a submitted order is valid before adding to the manager func (m *OrderManager) validate(newOrder *order.Submit) error { if newOrder == nil { - return errors.New("order cannot be nil") + return errNilOrder } if newOrder.Exchange == "" { - return errors.New("order exchange name must be specified") + return errExchangeNameIsEmpty } if err := newOrder.Validate(); err != nil { @@ -497,10 +519,12 @@ func (m *OrderManager) GetOrdersActive(f *order.Filter) ([]order.Detail, error) return m.orderStore.getActiveOrders(f), nil } +var errUnableToPlaceOrder = errors.New("cannot process order, order not placed") + // processSubmittedOrder adds a new order to the manager func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result order.SubmitResponse) (*OrderSubmitResponse, error) { if !result.IsOrderPlaced { - return nil, errors.New("order unable to be placed") + return nil, errUnableToPlaceOrder } id, err := uuid.NewV4() @@ -826,6 +850,9 @@ func (s *store) getByExchangeAndID(exchange, id string) (*order.Detail, error) { // updateExisting checks if an order exists in the orderstore // and then updates it func (s *store) updateExisting(od *order.Detail) error { + if od == nil { + return errNilOrder + } s.m.Lock() defer s.m.Unlock() r, ok := s.Orders[strings.ToLower(od.Exchange)] @@ -835,6 +862,14 @@ func (s *store) updateExisting(od *order.Detail) error { for x := range r { if r[x].ID == od.ID { r[x].UpdateOrderFromDetail(od) + if r[x].AssetType.IsFutures() { + err := s.futuresPositionController.TrackNewOrder(r[x]) + if err != nil { + if !errors.Is(err, order.ErrPositionClosed) { + return err + } + } + } return nil } } @@ -854,6 +889,14 @@ func (s *store) modifyExisting(id string, mod *order.Modify) error { for x := range r { if r[x].ID == id { r[x].UpdateOrderFromModify(mod) + if r[x].AssetType.IsFutures() { + err := s.futuresPositionController.TrackNewOrder(r[x]) + if err != nil { + if !errors.Is(err, order.ErrPositionClosed) { + return err + } + } + } return nil } } @@ -873,6 +916,14 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) } s.m.Lock() defer s.m.Unlock() + if od.AssetType.IsFutures() { + err = s.futuresPositionController.TrackNewOrder(od) + if err != nil { + if !errors.Is(err, order.ErrPositionClosed) { + return nil, err + } + } + } r, ok := s.Orders[lName] if !ok { od.GenerateInternalOrderID() @@ -952,7 +1003,7 @@ func (s *store) exists(det *order.Detail) bool { // Add Adds an order to the orderStore for tracking the lifecycle func (s *store) add(det *order.Detail) error { if det == nil { - return errors.New("order store: Order is nil") + return errNilOrder } _, err := s.exchangeManager.GetExchangeByName(det.Exchange) if err != nil { @@ -969,6 +1020,12 @@ func (s *store) add(det *order.Detail) error { orders = append(orders, det) s.Orders[strings.ToLower(det.Exchange)] = orders + if det.AssetType.IsFutures() { + err = s.futuresPositionController.TrackNewOrder(det) + if err != nil { + return err + } + } return nil } diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 95bed7637c7..9816ce00031 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -3,6 +3,7 @@ package engine import ( "context" "errors" + "strings" "sync" "testing" "time" @@ -1152,3 +1153,182 @@ func Test_getActiveOrders(t *testing.T) { t.Errorf("Test_getActiveOrders - Expected 0 results, got: %d", len(res)) } } + +func TestGetFuturesPositionsForExchange(t *testing.T) { + t.Parallel() + o := &OrderManager{} + cp := currency.NewPair(currency.BTC, currency.USDT) + _, err := o.GetFuturesPositionsForExchange("test", asset.Spot, cp) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + _, err = o.GetFuturesPositionsForExchange("test", asset.Spot, cp) + if !errors.Is(err, errFuturesTrackerNotSetup) { + t.Errorf("received '%v', expected '%v'", err, errFuturesTrackerNotSetup) + } + o.orderStore.futuresPositionController = order.SetupPositionController() + _, err = o.GetFuturesPositionsForExchange("test", asset.Spot, cp) + if !errors.Is(err, order.ErrNotFutureAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFutureAsset) + } + + _, err = o.GetFuturesPositionsForExchange("test", asset.Futures, cp) + if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) + } + o = nil + _, err = o.GetFuturesPositionsForExchange("test", asset.Futures, cp) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } +} + +func TestSubmitFakeOrder(t *testing.T) { + t.Parallel() + o := &OrderManager{} + resp := order.SubmitResponse{} + _, err := o.SubmitFakeOrder(nil, resp, false) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + + o.started = 1 + _, err = o.SubmitFakeOrder(nil, resp, false) + if !errors.Is(err, errNilOrder) { + t.Errorf("received '%v', expected '%v'", err, errNilOrder) + } + ord := &order.Submit{} + _, err = o.SubmitFakeOrder(ord, resp, false) + if !errors.Is(err, errExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", err, errExchangeNameIsEmpty) + } + ord.Exchange = testExchange + ord.AssetType = asset.Spot + ord.Pair = currency.NewPair(currency.BTC, currency.DOGE) + ord.Side = order.Buy + ord.Type = order.Market + ord.Amount = 1337 + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + exch.SetDefaults() + em.Add(exch) + o.orderStore.exchangeManager = em + + _, err = o.SubmitFakeOrder(ord, resp, false) + if !errors.Is(err, errUnableToPlaceOrder) { + t.Errorf("received '%v', expected '%v'", err, errUnableToPlaceOrder) + } + + resp.IsOrderPlaced = true + resp.FullyMatched = true + o.orderStore.commsManager = &CommunicationManager{} + o.orderStore.Orders = make(map[string][]*order.Detail) + _, err = o.SubmitFakeOrder(ord, resp, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } +} + +func TestGetOrdersSnapshot(t *testing.T) { + t.Parallel() + o := &OrderManager{} + o.GetOrdersSnapshot(order.AnyStatus) + o.started = 1 + o.orderStore.Orders = make(map[string][]*order.Detail) + o.orderStore.Orders[testExchange] = []*order.Detail{ + { + Status: order.Open, + }, + } + snap := o.GetOrdersSnapshot(order.Open) + if len(snap) != 1 { + t.Error("expected 1") + } + snap = o.GetOrdersSnapshot(order.Closed) + if len(snap) != 0 { + t.Error("expected 0") + } +} + +func TestUpdateExisting(t *testing.T) { + t.Parallel() + s := &store{} + s.Orders = make(map[string][]*order.Detail) + err := s.updateExisting(nil) + if !errors.Is(err, errNilOrder) { + t.Errorf("received '%v', expected '%v'", err, errNilOrder) + } + od := &order.Detail{Exchange: testExchange} + err = s.updateExisting(od) + if !errors.Is(err, ErrExchangeNotFound) { + t.Errorf("received '%v', expected '%v'", err, ErrExchangeNotFound) + } + s.Orders[strings.ToLower(testExchange)] = nil + err = s.updateExisting(od) + if !errors.Is(err, ErrOrderNotFound) { + t.Errorf("received '%v', expected '%v'", err, ErrOrderNotFound) + } + od.AssetType = asset.Futures + od.ID = "123" + od.Pair = currency.NewPair(currency.BTC, currency.USDT) + od.Side = order.Buy + od.Type = order.Market + od.Date = time.Now() + od.Amount = 1337 + s.Orders[strings.ToLower(testExchange)] = []*order.Detail{ + od, + } + s.futuresPositionController = order.SetupPositionController() + err = s.updateExisting(od) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + pos, err := s.futuresPositionController.GetPositionsForExchange(testExchange, asset.Futures, od.Pair) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + if len(pos) != 1 { + t.Error("expected 1") + } +} + +func TestOrderManagerExists(t *testing.T) { + t.Parallel() + o := &OrderManager{} + if o.Exists(nil) { + t.Error("expected false") + } + o.started = 1 + if o.Exists(nil) { + t.Error("expected false") + } + + o = nil + if o.Exists(nil) { + t.Error("expected false") + } +} + +func TestOrderManagerAdd(t *testing.T) { + t.Parallel() + o := &OrderManager{} + err := o.Add(nil) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + err = o.Add(nil) + if !errors.Is(err, errNilOrder) { + t.Errorf("received '%v', expected '%v'", err, errNilOrder) + } + + o = nil + err = o.Add(nil) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } +} diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index 4bbb73e937c..50dcbae5e27 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -37,11 +37,12 @@ type orderManagerConfig struct { // store holds all orders by exchange type store struct { - m sync.RWMutex - Orders map[string][]*order.Detail - commsManager iCommsManager - exchangeManager iExchangeManager - wg *sync.WaitGroup + m sync.RWMutex + Orders map[string][]*order.Detail + commsManager iCommsManager + exchangeManager iExchangeManager + wg *sync.WaitGroup + futuresPositionController *order.PositionController } // OrderManager processes and stores orders across enabled exchanges diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 810785cc78f..f71747f2090 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "path/filepath" + "sort" "strconv" "strings" "time" @@ -18,6 +19,7 @@ import ( grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/pquerna/otp/totp" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/common/file" @@ -4106,3 +4108,182 @@ func (s *RPCServer) CurrencyStateTradingPair(_ context.Context, r *gctrpc.Curren cp, asset.Item(r.Asset)) } + +// GetFuturesPositions returns pnl positions for an exchange asset pair +func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuturesPositionsRequest) (*gctrpc.GetFuturesPositionsResponse, error) { + exch, err := s.GetExchangeByName(r.Exchange) + if err != nil { + return nil, err + } + cp, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return nil, err + } + + a := asset.Item(r.Asset) + err = checkParams(r.Exchange, exch, a, cp) + if err != nil { + return nil, err + } + var start, end time.Time + if r.StartDate != "" { + start, err = time.Parse(common.SimpleTimeFormat, r.StartDate) + if err != nil { + return nil, err + } + } + if r.EndDate != "" { + end, err = time.Parse(common.SimpleTimeFormat, r.EndDate) + if err != nil { + return nil, err + } + } + err = common.StartEndTimeCheck(start, end) + if err != nil && !errors.Is(err, common.ErrDateUnset) { + return nil, err + } + + orders, err := exch.GetFuturesPositions(ctx, a, cp, start, end) + if err != nil { + return nil, err + } + sort.Slice(orders, func(i, j int) bool { + return orders[i].Date.Before(orders[j].Date) + }) + for i := range orders { + _, err = s.OrderManager.UpsertOrder(&orders[i]) + if err != nil { + if !errors.Is(err, order.ErrPositionClosed) { + return nil, err + } + } + } + pos, err := s.OrderManager.GetFuturesPositionsForExchange(r.Exchange, a, cp) + if err != nil { + return nil, err + } + response := &gctrpc.GetFuturesPositionsResponse{} + for i := range pos { + if r.PositionLimit > 0 && len(response.Positions) >= int(r.PositionLimit) { + break + } + stats := pos[i].GetStats() + response.TotalOrders += int64(len(stats.Orders)) + details := &gctrpc.FuturePosition{ + Status: stats.Status.String(), + CurrentDirection: stats.LatestDirection.String(), + UnrealisedPNL: stats.UnrealisedPNL.String(), + RealisedPNL: stats.RealisedPNL.String(), + } + if len(stats.PNLHistory) > 0 { + details.OpeningDate = stats.PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) + if stats.Status == order.Closed { + details.ClosingDate = stats.PNLHistory[len(stats.PNLHistory)-1].Time.Format(common.SimpleTimeFormatWithTimezone) + } + } + response.TotalRealisedPNL += stats.RealisedPNL.InexactFloat64() + response.TotalUnrealisedPNL += stats.UnrealisedPNL.InexactFloat64() + if !r.Verbose { + response.Positions = append(response.Positions, details) + continue + } + for j := range stats.Orders { + var trades []*gctrpc.TradeHistory + for k := range stats.Orders[j].Trades { + trades = append(trades, &gctrpc.TradeHistory{ + CreationTime: stats.Orders[j].Trades[k].Timestamp.Unix(), + Id: stats.Orders[j].Trades[k].TID, + Price: stats.Orders[j].Trades[k].Price, + Amount: stats.Orders[j].Trades[k].Amount, + Exchange: stats.Orders[j].Trades[k].Exchange, + AssetType: stats.Asset.String(), + OrderSide: stats.Orders[j].Trades[k].Side.String(), + Fee: stats.Orders[j].Trades[k].Fee, + Total: stats.Orders[j].Trades[k].Total, + }) + } + details.Orders = append(details.Orders, &gctrpc.OrderDetails{ + Exchange: stats.Orders[j].Exchange, + Id: stats.Orders[j].ID, + ClientOrderId: stats.Orders[j].ClientOrderID, + BaseCurrency: stats.Orders[j].Pair.Base.String(), + QuoteCurrency: stats.Orders[j].Pair.Quote.String(), + AssetType: stats.Orders[j].AssetType.String(), + OrderSide: stats.Orders[j].Side.String(), + OrderType: stats.Orders[j].Type.String(), + CreationTime: stats.Orders[j].Date.Unix(), + UpdateTime: stats.Orders[j].LastUpdated.Unix(), + Status: stats.Orders[j].Status.String(), + Price: stats.Orders[j].Price, + Amount: stats.Orders[j].Amount, + Fee: stats.Orders[j].Fee, + Cost: stats.Orders[j].Cost, + Trades: trades, + }) + } + response.Positions = append(response.Positions, details) + } + response.TotalPNL = response.TotalRealisedPNL + response.TotalUnrealisedPNL + return response, nil +} + +// GetCollateral returns the total collateral for an exchange's asset +// as exchanges can scale collateral and represent it in a singular currency, +// a user can opt to include a breakdown by currency +func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRequest) (*gctrpc.GetCollateralResponse, error) { + exch, err := s.GetExchangeByName(r.Exchange) + if err != nil { + return nil, err + } + + a := asset.Item(r.Asset) + err = checkParams(r.Exchange, exch, a, currency.Pair{}) + if err != nil { + return nil, err + } + ai, err := exch.FetchAccountInfo(ctx, a) + if err != nil { + return nil, err + } + + var calculators []order.CollateralCalculator + var acc account.SubAccount + if r.SubAccount != "" { + for i := range ai.Accounts { + if strings.EqualFold(r.SubAccount, ai.Accounts[i].ID) { + acc = ai.Accounts[i] + break + + } + } + } else if len(ai.Accounts) > 0 { + acc = ai.Accounts[0] + } + for i := range acc.Currencies { + calculators = append(calculators, order.CollateralCalculator{ + CalculateOffline: r.CalculateOffline, + CollateralCurrency: acc.Currencies[i].CurrencyName, + Asset: a, + CollateralAmount: decimal.NewFromFloat(acc.Currencies[i].TotalValue), + }) + } + + collateral, err := exch.CalculateTotalCollateral(ctx, calculators) + if err != nil { + return nil, err + } + + result := &gctrpc.GetCollateralResponse{ + TotalCollateral: collateral.TotalCollateral.String(), + } + if r.IncludeBreakdown { + for i := range collateral.BreakdownByCurrency { + result.CurrencyBreakdown = append(result.CurrencyBreakdown, &gctrpc.CollateralForCurrency{ + Currency: collateral.BreakdownByCurrency[i].Currency.String(), + ScaledCollateral: collateral.BreakdownByCurrency[i].Amount.String(), + ScaledToCurrency: collateral.BreakdownByCurrency[i].ValueCurrency.String(), + }) + } + } + return result, nil +} diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 236f7f5a144..892d65cdaac 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" @@ -93,14 +94,61 @@ func (f fExchange) GetHistoricCandlesExtended(ctx context.Context, p currency.Pa // FetchAccountInfo overrides testExchange's fetch account info function // to do the bare minimum required with no API calls or credentials required -func (f fExchange) FetchAccountInfo(ctx context.Context, a asset.Item) (account.Holdings, error) { +func (f fExchange) FetchAccountInfo(_ context.Context, a asset.Item) (account.Holdings, error) { return account.Holdings{ Exchange: f.GetName(), Accounts: []account.SubAccount{ { - ID: "1337", - AssetType: a, - Currencies: nil, + ID: "1337", + AssetType: a, + Currencies: []account.Balance{ + { + CurrencyName: currency.USD, + TotalValue: 1337, + }, + }, + }, + }, + }, nil +} + +// GetFuturesPositions overrides testExchange's GetFuturesPositions function +func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp currency.Pair, _ time.Time, _ time.Time) ([]order.Detail, error) { + return []order.Detail{ + { + Price: 1337, + Amount: 1337, + Fee: 1.337, + FeeAsset: currency.Code{}, + Exchange: f.GetName(), + ID: "test", + Side: order.Long, + Status: order.Open, + AssetType: a, + Date: time.Now(), + Pair: cp, + }, + }, nil +} + +// CalculateTotalCollateral overrides testExchange's CalculateTotalCollateral function +func (f fExchange) CalculateTotalCollateral(context.Context, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { + return &order.TotalCollateralResponse{ + TotalCollateral: decimal.NewFromInt(1337), + BreakdownByCurrency: []order.CollateralByCurrency{ + { + Currency: currency.USD, + Amount: decimal.NewFromInt(1330), + }, + { + Currency: currency.DOGE, + Amount: decimal.NewFromInt(10), + ValueCurrency: currency.USD, + }, + { + Currency: currency.XRP, + Amount: decimal.NewFromInt(-3), + ValueCurrency: currency.USD, }, }, }, nil @@ -1977,3 +2025,134 @@ func TestCurrencyStateTradingPair(t *testing.T) { t.Fatalf("received: %v, but expected: %v", err, nil) } } + +func TestGetFuturesPositions(t *testing.T) { + t.Parallel() + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + b := exch.GetBase() + b.Name = fakeExchangeName + b.Enabled = true + + cp, err := currency.NewPairFromString("btc-usd") + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } + fakeExchange := fExchange{ + IBotExchange: exch, + } + em.Add(fakeExchange) + var wg sync.WaitGroup + om, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + om.started = 1 + s := RPCServer{ + Engine: &Engine{ + ExchangeManager: em, + currencyStateManager: &CurrencyStateManager{ + started: 1, iExchangeManager: em, + }, + OrderManager: om, + }, + } + + r, err := s.GetFuturesPositions(context.Background(), &gctrpc.GetFuturesPositionsRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + Pair: &gctrpc.CurrencyPair{ + Delimiter: currency.DashDelimiter, + Base: cp.Base.String(), + Quote: cp.Quote.String(), + }, + Verbose: true, + }) + if err != nil { + t.Error(err) + } + if r == nil { + t.Fatal("expected not nil response") + } + if len(r.Positions) != 1 { + t.Fatal("expected 1 position") + } + if r.TotalOrders != 1 { + t.Fatal("expected 1 order") + } +} + +func TestGetCollateral(t *testing.T) { + t.Parallel() + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + b := exch.GetBase() + b.Name = fakeExchangeName + b.Enabled = true + + cp, err := currency.NewPairFromString("btc-usd") + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } + fakeExchange := fExchange{ + IBotExchange: exch, + } + em.Add(fakeExchange) + s := RPCServer{ + Engine: &Engine{ + ExchangeManager: em, + currencyStateManager: &CurrencyStateManager{ + started: 1, iExchangeManager: em, + }, + }, + } + + r, err := s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + }) + if err != nil { + t.Error(err) + } + if len(r.CurrencyBreakdown) > 0 { + t.Error("expected no breakdown") + } + + r, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + IncludeBreakdown: true, + SubAccount: "1337", + }) + if err != nil { + t.Error(err) + } + if len(r.CurrencyBreakdown) != 3 { + t.Error("expected 3 currencies") + } + if r.TotalCollateral != "1337" { + t.Error("expected 1337") + } +} diff --git a/exchanges/asset/asset.go b/exchanges/asset/asset.go index a12e7b000cc..772cd991753 100644 --- a/exchanges/asset/asset.go +++ b/exchanges/asset/asset.go @@ -117,3 +117,13 @@ func New(input string) (Item, error) { func UseDefault() Item { return Spot } + +// IsFutures checks if the asset type is a futures contract based asset +func (a Item) IsFutures() bool { + switch a { + case PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, + DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures: + return true + } + return false +} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 1ca828ce299..0c2d1c127f9 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" @@ -19,6 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/currencystate" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/stream" @@ -1455,3 +1457,29 @@ func (b *Base) UpdateCurrencyStates(ctx context.Context, a asset.Item) error { func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([]string, error) { return nil, common.ErrFunctionNotSupported } + +// CalculatePNL is an overridable function to allow PNL to be calculated on an +// open position +// It will also determine whether the position is considered to be liquidated +// For live trading, an overrided function may wish to confirm the liquidation by +// requesting the status of the asset +func (b *Base) CalculatePNL(*order.PNLCalculatorRequest) (*order.PNLResult, error) { + return nil, common.ErrNotYetImplemented +} + +// ScaleCollateral is an overridable function to determine how much +// collateral is usable in futures positions +func (b *Base) ScaleCollateral(context.Context, *order.CollateralCalculator) (decimal.Decimal, error) { + return decimal.Zero, common.ErrNotYetImplemented +} + +// CalculateTotalCollateral takes in n collateral calculators to determine an overall +// standing in a singular currency. See FTX's implementation +func (b *Base) CalculateTotalCollateral(context.Context, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { + return nil, common.ErrNotYetImplemented +} + +// GetFuturesPositions returns futures positions according to the provided parameters +func (b *Base) GetFuturesPositions(context.Context, asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) { + return nil, common.ErrNotYetImplemented +} diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index aee9cf84aeb..a351c6873b3 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -26,6 +26,7 @@ import ( // FTX is the overarching type across this package type FTX struct { exchange.Base + collateralWeight CollateralWeightHolder } const ( @@ -139,6 +140,8 @@ var ( errSubaccountTransferSourceDestinationMustNotBeEqual = errors.New("subaccount transfer source and destination must not be the same value") errUnrecognisedOrderStatus = errors.New("unrecognised order status received") errInvalidOrderAmounts = errors.New("filled amount should not exceed order amount") + errCollateralCurrencyNotFound = errors.New("no collateral scaling information found") + errCollateralIMFMissing = errors.New("cannot scale collateral, missing IMF information") validResolutionData = []int64{15, 60, 300, 900, 3600, 14400, 86400} ) @@ -839,13 +842,17 @@ func (f *FTX) DeleteTriggerOrder(ctx context.Context, orderID string) (string, e } // GetFills gets fills' data -func (f *FTX) GetFills(ctx context.Context, market, limit string, startTime, endTime time.Time) ([]FillsData, error) { +func (f *FTX) GetFills(ctx context.Context, market currency.Pair, limit string, startTime, endTime time.Time) ([]FillsData, error) { resp := struct { Data []FillsData `json:"result"` }{} params := url.Values{} - if market != "" { - params.Set("market", market) + if !market.IsEmpty() { + fp, err := f.FormatExchangeCurrency(market, asset.Futures) + if err != nil { + return nil, err + } + params.Set("market", fp.String()) } if limit != "" { params.Set("limit", limit) @@ -1468,3 +1475,203 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err } return limits, nil } + +func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64, side order.Side) (float64, error) { + collateralWeight, ok := f.collateralWeight[code.Upper().String()] + if !ok { + return 0, errCoinMustBeSpecified + } + return collateralWeight.Total * positionSize, nil +} + +// LoadCollateralWeightings sets the collateral weights for +// currencies supported by FTX +func (f *FTX) LoadCollateralWeightings() error { + f.collateralWeight = make(map[string]CollateralWeight) + // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral + // sets default, then uses the latest from FTX + f.collateralWeight.load("1INCH", 0.9, 0.85, 0.0005) + f.collateralWeight.load("AAPL", 0.9, 0.85, 0.005) + f.collateralWeight.load("AAVE", 0.9, 0.85, 0.0025) + f.collateralWeight.load("ABNB", 0.9, 0.85, 0.005) + f.collateralWeight.load("ACB", 0.9, 0.85, 0.0025) + f.collateralWeight.load("ALPHA", 0.9, 0.85, 0.00025) + f.collateralWeight.load("AMC", 0.9, 0.85, 0.0025) + f.collateralWeight.load("AMD", 0.9, 0.85, 0.005) + f.collateralWeight.load("AMZN", 0.9, 0.85, 0.03) + f.collateralWeight.load("APHA", 0.9, 0.85, 0.001) + f.collateralWeight.load("ARKK", 0.9, 0.85, 0.005) + f.collateralWeight.load("AUD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("BABA", 0.9, 0.85, 0.01) + f.collateralWeight.load("BADGER", 0.85, 0.8, 0.0025) + f.collateralWeight.load("BAND", 0.85, 0.8, 0.001) + f.collateralWeight.load("BAO", 0.85, 0.8, 0.000025) + f.collateralWeight.load("BB", 0.9, 0.85, 0.0025) + f.collateralWeight.load("BCH", 0.95, 0.9, 0.0008) + f.collateralWeight.load("BILI", 0.9, 0.85, 0.005) + f.collateralWeight.load("BITW", 0.9, 0.85, 0.005) + f.collateralWeight.load("BNB", 0.95, 0.9, 0.0005) + f.collateralWeight.load("BNT", 0.9, 0.85, 0.0025) + f.collateralWeight.load("BNTX", 0.9, 0.85, 0.005) + f.collateralWeight.load("BRL", 0.99, 0.98, 0.00001) + f.collateralWeight.load("BRZ", 0.99, 0.98, 0.00001) + f.collateralWeight.load("BTC", 0.975, 0.95, 0.002) + f.collateralWeight.load("BTMX", 0.7, 0.65, 0.0008) + f.collateralWeight.load("BUSD", 1, 1, 0) + f.collateralWeight.load("BVOL", 0.85, 0.8, 0.005) + f.collateralWeight.load("BYND", 0.9, 0.85, 0.0075) + f.collateralWeight.load("CAD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("CEL", 0.85, 0.8, 0.001) + f.collateralWeight.load("CGC", 0.9, 0.85, 0.0025) + f.collateralWeight.load("CHF", 0.99, 0.98, 0.00001) + f.collateralWeight.load("COIN", 0.85, 0.8, 0.01) + f.collateralWeight.load("COMP", 0.9, 0.85, 0.002) + f.collateralWeight.load("COPE", 0.6, 0.55, 0.02) + f.collateralWeight.load("CRON", 0.9, 0.85, 0.001) + f.collateralWeight.load("CUSDT", 0.9, 0.85, 0.00001) + f.collateralWeight.load("DAI", 0.9, 0.85, 0.00005) + f.collateralWeight.load("DOGE", 0.95, 0.9, 0.00002) + f.collateralWeight.load("ETH", 0.95, 0.9, 0.0004) + f.collateralWeight.load("STETH", 0.9, 0.85, 0.0012) + f.collateralWeight.load("ETHE", 0.9, 0.85, 0.0025) + f.collateralWeight.load("EUR", 0.99, 0.98, 0.00001) + f.collateralWeight.load("FB", 0.9, 0.85, 0.01) + f.collateralWeight.load("FIDA", 0.85, 0.8, 0.001) + f.collateralWeight.load("FTM", 0.85, 0.8, 0.0005) + f.collateralWeight.load("FTT", 0.95, 0.95, 0.0005) + f.collateralWeight.load("GBP", 0.99, 0.98, 0.00001) + f.collateralWeight.load("GBTC", 0.9, 0.85, 0.0025) + f.collateralWeight.load("GDX", 0.9, 0.85, 0.0025) + f.collateralWeight.load("GDXJ", 0.9, 0.85, 0.005) + f.collateralWeight.load("GLD", 0.9, 0.85, 0.005) + f.collateralWeight.load("GLXY", 0.9, 0.85, 0.005) + f.collateralWeight.load("GME", 0.9, 0.85, 0.005) + f.collateralWeight.load("GOOGL", 0.9, 0.85, 0.025) + f.collateralWeight.load("GRT", 0.9, 0.85, 0.00025) + f.collateralWeight.load("HKD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("HOLY", 0.9, 0.85, 0.0005) + f.collateralWeight.load("HOOD", 0.85, 0.8, 0.005) + f.collateralWeight.load("HT", 0.9, 0.85, 0.0003) + f.collateralWeight.load("HUSD", 1, 1, 0) + f.collateralWeight.load("HXRO", 0.85, 0.8, 0.001) + f.collateralWeight.load("IBVOL", 0.85, 0.8, 0.015) + f.collateralWeight.load("KIN", 0.85, 0.8, 0.000008) + f.collateralWeight.load("KNC", 0.95, 0.9, 0.001) + f.collateralWeight.load("LEO", 0.85, 0.8, 0.001) + f.collateralWeight.load("LINK", 0.95, 0.9, 0.0004) + f.collateralWeight.load("LRC", 0.85, 0.8, 0.0005) + f.collateralWeight.load("LTC", 0.95, 0.9, 0.0004) + f.collateralWeight.load("MATIC", 0.85, 0.8, 0.00004) + f.collateralWeight.load("MKR", 0.9, 0.85, 0.007) + f.collateralWeight.load("MOB", 0.6, 0.55, 0.005) + f.collateralWeight.load("MRNA", 0.9, 0.85, 0.005) + f.collateralWeight.load("MSTR", 0.9, 0.85, 0.008) + f.collateralWeight.load("NFLX", 0.9, 0.85, 0.01) + f.collateralWeight.load("NIO", 0.9, 0.85, 0.004) + f.collateralWeight.load("NOK", 0.9, 0.85, 0.001) + f.collateralWeight.load("NVDA", 0.9, 0.85, 0.01) + f.collateralWeight.load("OKB", 0.9, 0.85, 0.0003) + f.collateralWeight.load("OMG", 0.85, 0.8, 0.001) + f.collateralWeight.load("USDP", 1, 1, 0) + f.collateralWeight.load("PAXG", 0.95, 0.9, 0.002) + f.collateralWeight.load("PENN", 0.9, 0.85, 0.005) + f.collateralWeight.load("PFE", 0.9, 0.85, 0.004) + f.collateralWeight.load("PYPL", 0.9, 0.85, 0.008) + f.collateralWeight.load("RAY", 0.85, 0.8, 0.0005) + f.collateralWeight.load("REN", 0.9, 0.85, 0.00025) + f.collateralWeight.load("RSR", 0.85, 0.8, 0.0001) + f.collateralWeight.load("RUNE", 0.85, 0.8, 0.001) + f.collateralWeight.load("SECO", 0.9, 0.85, 0.0005) + f.collateralWeight.load("SGD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("SLV", 0.9, 0.85, 0.0025) + f.collateralWeight.load("SNX", 0.85, 0.8, 0.001) + f.collateralWeight.load("SOL", 0.9, 0.85, 0.0004) + f.collateralWeight.load("STSOL", 0.9, 0.85, 0.0004) + f.collateralWeight.load("MSOL", 0.9, 0.85, 0.0004) + f.collateralWeight.load("SPY", 0.9, 0.85, 0.01) + f.collateralWeight.load("SQ", 0.9, 0.85, 0.008) + f.collateralWeight.load("SRM", 0.9, 0.85, 0.0005) + f.collateralWeight.load("SUSHI", 0.95, 0.9, 0.001) + f.collateralWeight.load("SXP", 0.9, 0.85, 0.0005) + f.collateralWeight.load("TLRY", 0.9, 0.85, 0.001) + f.collateralWeight.load("TOMO", 0.85, 0.8, 0.0005) + f.collateralWeight.load("TRX", 0.9, 0.85, 0.00001) + f.collateralWeight.load("TRY", 0.99, 0.98, 0.00001) + f.collateralWeight.load("TRYB", 0.9, 0.85, 0.00001) + f.collateralWeight.load("TSLA", 0.9, 0.85, 0.01) + f.collateralWeight.load("TSM", 0.9, 0.85, 0.015) + f.collateralWeight.load("TUSD", 1, 1, 0) + f.collateralWeight.load("TWTR", 0.9, 0.85, 0.004) + f.collateralWeight.load("UBER", 0.9, 0.85, 0.004) + f.collateralWeight.load("UNI", 0.95, 0.9, 0.001) + f.collateralWeight.load("USD", 1, 1, 0) + f.collateralWeight.load("USDC", 1, 1, 0) + f.collateralWeight.load("USDT", 0.975, 0.95, 0.00001) + f.collateralWeight.load("USO", 0.9, 0.85, 0.0025) + f.collateralWeight.load("WBTC", 0.975, 0.95, 0.005) + f.collateralWeight.load("WUSDC", 1, 1, 0) + f.collateralWeight.load("WUSDT", 0.975, 0.95, 0.00001) + f.collateralWeight.load("XAUT", 0.95, 0.9, 0.002) + f.collateralWeight.load("XRP", 0.95, 0.9, 0.00002) + f.collateralWeight.load("YFI", 0.9, 0.85, 0.015) + f.collateralWeight.load("ZAR", 0.99, 0.98, 0.00001) + f.collateralWeight.load("ZM", 0.9, 0.85, 0.01) + f.collateralWeight.load("ZRX", 0.85, 0.8, 0.001) + + if !f.GetAuthenticatedAPISupport(exchange.RestAuthentication) { + return nil + } + ctx := context.Background() + coins, err := f.GetCoins(ctx) + if err != nil { + return err + } + for i := range coins { + if !coins[i].Collateral { + continue + } + f.collateralWeight.loadTotal(coins[i].ID, coins[i].CollateralWeight) + } + + futures, err := f.GetFutures(ctx) + if err != nil { + return err + } + for i := range futures { + f.collateralWeight.loadIMF(futures[i].Underlying, futures[i].IMFFactor) + } + + return nil +} + +func (c CollateralWeightHolder) isLoaded() bool { + return len(c) > 0 +} + +func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { + butts, ok := c[code] + if !ok { + butts = CollateralWeight{Total: weighting} + } else { + butts.Total = weighting + } + c[code] = butts +} + +func (c CollateralWeightHolder) loadIMF(code string, imf float64) { + butts, ok := c[code] + if !ok { + butts = CollateralWeight{IMFFactor: imf} + } else { + butts.IMFFactor = imf + } + c[code] = butts +} + +func (c CollateralWeightHolder) load(code string, initial, total, imfFactor float64) { + c[code] = CollateralWeight{ + Initial: initial, + Total: total, + IMFFactor: imfFactor, + } +} diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 5e5dda5b879..9f4ac5738bd 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -3,12 +3,15 @@ package ftx import ( "context" "errors" + "fmt" "log" + "math" "os" "sync" "testing" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" @@ -26,7 +29,7 @@ const ( apiSecret = "" subaccount = "" canManipulateRealOrders = false - spotPair = "FTT/BTC" + spotPairStr = "FTT/BTC" futuresPair = "DOGE-PERP" testLeverageToken = "ADAMOON" @@ -38,7 +41,10 @@ const ( authEndTime = validFTTBTCEndTime ) -var f FTX +var ( + f FTX + spotPair = currency.NewPair(currency.FTT, currency.BTC) +) func TestMain(m *testing.M) { f.SetDefaults() @@ -115,7 +121,7 @@ func TestGetHistoricalIndex(t *testing.T) { func TestGetMarket(t *testing.T) { t.Parallel() - _, err := f.GetMarket(context.Background(), spotPair) + _, err := f.GetMarket(context.Background(), spotPairStr) if err != nil { t.Error(err) } @@ -123,7 +129,7 @@ func TestGetMarket(t *testing.T) { func TestGetOrderbook(t *testing.T) { t.Parallel() - _, err := f.GetOrderbook(context.Background(), spotPair, 5) + _, err := f.GetOrderbook(context.Background(), spotPairStr, 5) if err != nil { t.Error(err) } @@ -137,13 +143,13 @@ func TestGetTrades(t *testing.T) { t.Error("empty market should return an error") } _, err = f.GetTrades(context.Background(), - spotPair, validFTTBTCEndTime, validFTTBTCStartTime, 5) + spotPairStr, validFTTBTCEndTime, validFTTBTCStartTime, 5) if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } // test optional params var trades []TradeData - trades, err = f.GetTrades(context.Background(), spotPair, 0, 0, 0) + trades, err = f.GetTrades(context.Background(), spotPairStr, 0, 0, 0) if err != nil { t.Error(err) } @@ -151,7 +157,7 @@ func TestGetTrades(t *testing.T) { t.Error("default limit should return 20 items") } trades, err = f.GetTrades(context.Background(), - spotPair, validFTTBTCStartTime, validFTTBTCEndTime, 5) + spotPairStr, validFTTBTCStartTime, validFTTBTCEndTime, 5) if err != nil { t.Error(err) } @@ -159,7 +165,7 @@ func TestGetTrades(t *testing.T) { t.Error("limit of 5 should return 5 items") } trades, err = f.GetTrades(context.Background(), - spotPair, invalidFTTBTCStartTime, invalidFTTBTCEndTime, 5) + spotPairStr, invalidFTTBTCStartTime, invalidFTTBTCEndTime, 5) if err != nil { t.Error(err) } @@ -178,19 +184,19 @@ func TestGetHistoricalData(t *testing.T) { } // test empty resolution _, err = f.GetHistoricalData(context.Background(), - spotPair, 0, 5, time.Time{}, time.Time{}) + spotPairStr, 0, 5, time.Time{}, time.Time{}) if err == nil { t.Error("empty resolution should return an error") } _, err = f.GetHistoricalData(context.Background(), - spotPair, 86400, 5, time.Unix(validFTTBTCEndTime, 0), + spotPairStr, 86400, 5, time.Unix(validFTTBTCEndTime, 0), time.Unix(validFTTBTCStartTime, 0)) if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } var o []OHLCVData o, err = f.GetHistoricalData(context.Background(), - spotPair, 86400, 5, time.Time{}, time.Time{}) + spotPairStr, 86400, 5, time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -198,7 +204,7 @@ func TestGetHistoricalData(t *testing.T) { t.Error("limit of 5 should return 5 items") } o, err = f.GetHistoricalData(context.Background(), - spotPair, 86400, 5, time.Unix(invalidFTTBTCStartTime, 0), + spotPairStr, 86400, 5, time.Unix(invalidFTTBTCStartTime, 0), time.Unix(invalidFTTBTCEndTime, 0)) if err != nil { t.Error(err) @@ -230,15 +236,6 @@ func TestGetFutureStats(t *testing.T) { if err != nil { t.Error(err) } - - future, err := f.GetFutureStats(context.Background(), "BTC-MOVE-2021Q4") - if err != nil { - t.Error(err) - } - - if future.Greeks == nil { - t.Fatal("no greeks returned for futures contract") - } } func TestGetFundingRates(t *testing.T) { @@ -271,6 +268,7 @@ func TestGetPositions(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } + f.Verbose = true _, err := f.GetPositions(context.Background()) if err != nil { t.Error(err) @@ -520,7 +518,7 @@ func TestGetOpenOrders(t *testing.T) { if err != nil { t.Error(err) } - _, err = f.GetOpenOrders(context.Background(), spotPair) + _, err = f.GetOpenOrders(context.Background(), spotPairStr) if err != nil { t.Error(err) } @@ -537,12 +535,12 @@ func TestFetchOrderHistory(t *testing.T) { t.Error(err) } _, err = f.FetchOrderHistory(context.Background(), - spotPair, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), "2") + spotPairStr, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), "2") if err != nil { t.Error(err) } _, err = f.FetchOrderHistory(context.Background(), - spotPair, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), "2") + spotPairStr, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), "2") if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } @@ -558,7 +556,7 @@ func TestGetOpenTriggerOrders(t *testing.T) { if err != nil { t.Error(err) } - _, err = f.GetOpenTriggerOrders(context.Background(), spotPair, "") + _, err = f.GetOpenTriggerOrders(context.Background(), spotPairStr, "") if err != nil { t.Error(err) } @@ -586,12 +584,12 @@ func TestGetTriggerOrderHistory(t *testing.T) { t.Error(err) } _, err = f.GetTriggerOrderHistory(context.Background(), - spotPair, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1") + spotPairStr, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1") if err != nil { t.Error(err) } _, err = f.GetTriggerOrderHistory(context.Background(), - spotPair, + spotPairStr, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), order.Buy.Lower(), @@ -601,7 +599,7 @@ func TestGetTriggerOrderHistory(t *testing.T) { t.Error(err) } _, err = f.GetTriggerOrderHistory(context.Background(), - spotPair, + spotPairStr, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), order.Buy.Lower(), @@ -618,7 +616,7 @@ func TestOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } _, err := f.Order(context.Background(), - spotPair, + spotPairStr, order.Buy.Lower(), "limit", false, false, false, @@ -635,7 +633,7 @@ func TestSubmitOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set correctly") } - currencyPair, err := currency.NewPairFromString(spotPair) + currencyPair, err := currency.NewPairFromString(spotPairStr) if err != nil { t.Fatal(err) } @@ -661,7 +659,7 @@ func TestTriggerOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } _, err := f.TriggerOrder(context.Background(), - spotPair, + spotPairStr, order.Buy.Lower(), order.Stop.Lower(), "", "", @@ -677,7 +675,7 @@ func TestCancelOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set correctly") } - currencyPair, err := currency.NewPairFromString(spotPair) + currencyPair, err := currency.NewPairFromString(spotPairStr) if err != nil { t.Fatal(err) } @@ -736,7 +734,8 @@ func TestGetFills(t *testing.T) { t.Skip() } // optional params - _, err := f.GetFills(context.Background(), "", "", time.Time{}, time.Time{}) + + _, err := f.GetFills(context.Background(), spotPair, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -1262,7 +1261,7 @@ func TestGetOTCQuoteStatus(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("API keys required but not set, skipping test") } - _, err := f.GetOTCQuoteStatus(context.Background(), spotPair, "1") + _, err := f.GetOTCQuoteStatus(context.Background(), spotPairStr, "1") if err != nil { t.Error(err) } @@ -1720,3 +1719,205 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { err) } } + +func TestScaleCollateral(t *testing.T) { + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + accountInfo, err := f.GetAccountInfo(context.Background()) + if err != nil { + t.Error(err) + } + walletInfo, err := f.GetAllWalletBalances(context.Background()) + if err != nil { + t.Error(err) + } + localScaling := 0.0 + liquidationScaling := 0.0 + providedUSDValue := 0.0 + for _, v := range walletInfo { + for v2 := range v { + coin := v[v2].Coin + if coin == "USD" { + localScaling += v[v2].Total + providedUSDValue += v[v2].USDValue + liquidationScaling += v[v2].Total + continue + } + var tick MarketData + tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) + if err != nil { + t.Error(err) + } + var scaled decimal.Decimal + scaled, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + CalculateOffline: true, + }) + if err != nil { + if errors.Is(err, errCollateralCurrencyNotFound) { + continue + } + t.Error(err) + } + localScaling += scaled.InexactFloat64() + providedUSDValue += v[v2].USDValue + + scaled, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + IsLiquidating: true, + CalculateOffline: true, + }) + if err != nil { + t.Error(err) + } + liquidationScaling += scaled.InexactFloat64() + + _, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + }) + if err != nil { + t.Error(err) + } + } + } + if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { + t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) + } +} + +func TestCalculateTotalCollateral(t *testing.T) { + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + walletInfo, err := f.GetAllWalletBalances(context.Background()) + if err != nil { + t.Error(err) + } + var scales []order.CollateralCalculator + for _, v := range walletInfo { + for v2 := range v { + coin := v[v2].Coin + if coin == "USD" { + total := decimal.NewFromFloat(v[v2].Total) + scales = append(scales, order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: total, + USDPrice: total, + CalculateOffline: true, + }) + continue + } + var tick MarketData + tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) + if err != nil { + t.Error(err) + } + scales = append(scales, order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + CalculateOffline: true, + }) + } + } + total, err := f.CalculateTotalCollateral(context.Background(), scales) + if err != nil { + t.Error(err) + } + localScaling := total.TotalCollateral.InexactFloat64() + accountInfo, err := f.GetAccountInfo(context.Background()) + if err != nil { + t.Error(err) + } + if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { + t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) + } + + for i := range scales { + scales[i].CalculateOffline = false + } + _, err = f.CalculateTotalCollateral(context.Background(), scales) + if err != nil { + t.Error(err) + } +} + +func TestCalculatePNL(t *testing.T) { + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + f.Verbose = true + pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) + positions, err := f.GetFuturesPositions(context.Background(), asset.Futures, pair, time.Date(2021, 1, 6, 4, 28, 0, 0, time.UTC), time.Date(2021, 12, 31, 4, 32, 0, 0, time.UTC)) + var orders []order.Detail + for i := range positions { + orders = append(orders, order.Detail{ + Side: positions[i].Side, + Pair: pair, + ID: fmt.Sprintf("%v", positions[i].ID), + Price: positions[i].Price, + Amount: positions[i].Amount, + AssetType: asset.Futures, + Exchange: f.Name, + Fee: positions[i].Fee, + Date: positions[i].Date, + }) + } + + exch := f.Name + item := asset.Futures + setup := &order.MultiPositionTrackerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + UseExchangePNLCalculation: true, + ExchangePNLCalculation: &f, + } + p, err := order.SetupMultiPositionTracker(setup) + if err != nil { + t.Error(err) + } + for i := range orders { + err = p.TrackNewOrder(&orders[i]) + if err != nil { + t.Error(err) + } + } + results := p.GetPositions() + for i := range results { + _, err = results[i].GetLatestPNLSnapshot() + if err != nil { + t.Error(err) + } + } +} + +func TestGetFuturesPositions(t *testing.T) { + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + cp := currency.NewPair(currency.BTC, currency.NewCode("1231")) + start := time.Now().Add(-time.Hour * 24 * 365) + end := time.Now() + a := asset.Futures + _, err := f.GetFuturesPositions(context.Background(), a, cp, start, end) + if err != nil { + t.Error(err) + } +} diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index d9124935cdb..b7884fa5315 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -163,11 +163,6 @@ type FutureStatsData struct { PredictedExpirationPrice float64 `json:"predictedExpirationPrice"` OpenInterest float64 `json:"openInterest"` StrikePrice float64 `json:"strikePrice"` - Greeks *struct { - ImpliedVolatility float64 `json:"impliedVolatility"` - Delta float64 `json:"delta"` - Gamma float64 `json:"gamma"` - } `json:"greeks"` } // FundingRatesData stores data on funding rates @@ -184,21 +179,26 @@ type IndexWeights struct { // PositionData stores data of an open position type PositionData struct { + CollateralUsed float64 `json:"collateralUsed"` Cost float64 `json:"cost"` + CumulativeBuySize float64 `json:"cumulativeBuySize"` + CumulativeSellSize float64 `json:"cumulativeSellSize"` EntryPrice float64 `json:"entryPrice"` + EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"` Future string `json:"future"` InitialMarginRequirement float64 `json:"initialMarginRequirement"` LongOrderSize float64 `json:"longOrderSize"` MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"` NetSize float64 `json:"netSize"` OpenSize float64 `json:"openSize"` - RealizedPnL float64 `json:"realizedPnL"` + RealizedPNL float64 `json:"realizedPnl"` + RecentAverageOpenPrice float64 `json:"recentAverageOpenPrice"` + RecentBreakEvenPrice float64 `json:"recentBreakEvenPrice"` + RecentPnl float64 `json:"recentPnl"` ShortOrderSize float64 `json:"shortOrderSize"` Side string `json:"side"` Size float64 `json:"size"` - UnrealizedPnL float64 `json:"unrealizedPnL"` - CollateralUsed float64 `json:"collateralUsed"` - EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"` + UnrealizedPNL float64 `json:"unrealizedPnl"` } // AccountInfoData stores account data @@ -880,3 +880,14 @@ type StakeReward struct { Status string `json:"status"` Time time.Time `json:"time"` } + +// CollateralWeightHolder stores collateral weights over the lifecycle of the application +type CollateralWeightHolder map[string]CollateralWeight + +// CollateralWeight holds collateral information provided by FTX +// it is used to scale collateral when the currency is not in USD +type CollateralWeight struct { + Initial float64 + Total float64 + IMFFactor float64 +} diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index 4326592c42c..f6787fb49d6 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -53,6 +53,7 @@ func parseRaw(t *testing.T, input string) interface{} { Fills: fills, }, }, + CollateralWeightHolder{}, } if err := x.wsHandleData([]byte(input)); err != nil { @@ -358,7 +359,7 @@ func TestParsingMarketsData(t *testing.T) { "future": { "name": "ADA-0626", "underlying": "ADA", - "description": "Cardano June 2020 Futures", + "description": "Cardano June 2020 FuturesTracker", "type": "future", "expiry": "2020-06-26T003:00:00+00:00", "perpetual": false, "expired": false, diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index c5564061185..213cacfef11 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -4,12 +4,14 @@ import ( "context" "errors" "fmt" + "math" "sort" "strconv" "strings" "sync" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" @@ -184,25 +186,40 @@ func (f *FTX) Setup(exch *config.Exchange) error { return err } - err = f.Websocket.Setup(&stream.WebsocketSetup{ - ExchangeConfig: exch, - DefaultURL: ftxWSURL, - RunningURL: wsEndpoint, - Connector: f.WsConnect, - Subscriber: f.Subscribe, - Unsubscriber: f.Unsubscribe, - GenerateSubscriptions: f.GenerateDefaultSubscriptions, - Features: &f.Features.Supports.WebsocketCapabilities, - TradeFeed: f.Features.Enabled.TradeFeed, - FillsFeed: f.Features.Enabled.FillsFeed, - }) - if err != nil { - return err + if exch.Websocket != nil && *exch.Websocket { + err = f.Websocket.Setup(&stream.WebsocketSetup{ + ExchangeConfig: exch, + DefaultURL: ftxWSURL, + RunningURL: wsEndpoint, + Connector: f.WsConnect, + Subscriber: f.Subscribe, + Unsubscriber: f.Unsubscribe, + GenerateSubscriptions: f.GenerateDefaultSubscriptions, + Features: &f.Features.Supports.WebsocketCapabilities, + TradeFeed: f.Features.Enabled.TradeFeed, + FillsFeed: f.Features.Enabled.FillsFeed, + }) + if err != nil { + return err + } } - return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - }) + + if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { + err = f.LoadCollateralWeightings() + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to store collateral weightings. Err: %s", + f.Name, + err) + } + } + if exch.Websocket != nil && *exch.Websocket { + return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + }) + } + return nil } // Start starts the FTX go routine @@ -247,6 +264,7 @@ func (f *FTX) Run() { f.Name, err) } + } // FetchTradablePairs returns a list of the exchanges tradable pairs @@ -800,7 +818,7 @@ func (s *OrderData) GetCompatible(ctx context.Context, f *FTX) (OrderVars, error } // GetOrderInfo returns order information based on order ID -func (f *FTX) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) { +func (f *FTX) GetOrderInfo(ctx context.Context, orderID string, _ currency.Pair, _ asset.Item) (order.Detail, error) { var resp order.Detail orderData, err := f.GetOrderStatus(ctx, orderID) if err != nil { @@ -1254,3 +1272,157 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur } return availableChains, nil } + +// CalculatePNL determines the PNL of a given position based on the PNLCalculatorRequest +func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { + if pnl == nil { + return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) + } + var result order.PNLResult + result.Time = pnl.Time + if pnl.CalculateOffline { + // PNLCalculator matches FTX's pnl calculation method + calc := order.PNLCalculator{} + return calc.CalculatePNL(pnl) + } + + ep := pnl.EntryPrice.InexactFloat64() + info, err := f.GetAccountInfo(context.Background()) + if err != nil { + return nil, err + } + if info.Liquidating || info.Collateral == 0 { + result.IsLiquidated = true + return &result, order.ErrPositionLiquidated + } + for i := range info.Positions { + var pair currency.Pair + pair, err = currency.NewPairFromString(info.Positions[i].Future) + if err != nil { + return nil, err + } + if !pnl.Pair.Equal(pair) { + continue + } + if info.Positions[i].EntryPrice != ep { + continue + } + result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) + result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) + result.Price = decimal.NewFromFloat(info.Positions[i].Cost) + return &result, nil + } + // order no longer active, use offline calculation + pnl.CalculateOffline = true + return f.CalculatePNL(pnl) +} + +// ScaleCollateral takes your totals and scales them according to FTX's rules +func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalculator) (decimal.Decimal, error) { + var result decimal.Decimal + if calc.CalculateOffline { + if calc.CollateralCurrency == currency.USD { + return calc.CollateralAmount, nil + } + collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] + if !ok { + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) + } + if calc.CollateralAmount.IsPositive() { + if collateralWeight.IMFFactor == 0 { + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralIMFMissing) + } + var scaling decimal.Decimal + if calc.IsLiquidating { + scaling = decimal.NewFromFloat(collateralWeight.Total) + } else { + scaling = decimal.NewFromFloat(collateralWeight.Initial) + } + weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.IMFFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) + result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) + } else { + result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) + } + return result, nil + } + wallet, err := f.GetCoins(ctx) + if err != nil { + return decimal.Zero, err + } + balances, err := f.GetBalances(ctx) + if err != nil { + return decimal.Zero, err + } + for i := range wallet { + if currency.NewCode(wallet[i].ID) != calc.CollateralCurrency { + continue + } + for j := range balances { + if currency.NewCode(balances[j].Coin) != calc.CollateralCurrency { + continue + } + scaled := wallet[i].CollateralWeight * balances[j].USDValue + result = decimal.NewFromFloat(scaled) + return result, nil + } + } + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) +} + +// CalculateTotalCollateral scales collateral and determines how much collateral you can use for positions +func (f *FTX) CalculateTotalCollateral(ctx context.Context, collateralAssets []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { + var result order.TotalCollateralResponse + for i := range collateralAssets { + collateral, err := f.ScaleCollateral(ctx, &collateralAssets[i]) + if err != nil { + if errors.Is(err, errCollateralCurrencyNotFound) { + log.Error(log.ExchangeSys, err) + continue + } + return nil, err + } + result.TotalCollateral = result.TotalCollateral.Add(collateral) + result.BreakdownByCurrency = append(result.BreakdownByCurrency, order.CollateralByCurrency{ + Currency: collateralAssets[i].CollateralCurrency, + Amount: collateral, + ValueCurrency: currency.USD, + }) + } + return &result, nil +} + +// GetFuturesPositions returns all futures positions within provided params +func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency.Pair, start, end time.Time) ([]order.Detail, error) { + if !a.IsFutures() { + return nil, fmt.Errorf("%w futures asset type only", common.ErrFunctionNotSupported) + } + fills, err := f.GetFills(ctx, cp, "200", start, end) + if err != nil { + return nil, err + } + sort.Slice(fills, func(i, j int) bool { + return fills[i].Time.Before(fills[j].Time) + }) + var resp []order.Detail + var side order.Side + for i := range fills { + price := fills[i].Price + side, err = order.StringToOrderSide(fills[i].Side) + if err != nil { + return nil, err + } + resp = append(resp, order.Detail{ + Side: side, + Pair: cp, + ID: fmt.Sprintf("%v", fills[i].ID), + Price: price, + Amount: fills[i].Size, + AssetType: a, + Exchange: f.Name, + Fee: fills[i].Fee, + Date: fills[i].Time, + }) + } + + return resp, nil +} diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index e97baecddb7..70cf3b74fab 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -80,6 +80,11 @@ type IBotExchange interface { GetHistoricCandlesExtended(ctx context.Context, p currency.Pair, a asset.Item, timeStart, timeEnd time.Time, interval kline.Interval) (kline.Item, error) DisableRateLimiter() error EnableRateLimiter() error + CurrencyStateManagement + + order.PNLCalculation + order.CollateralManagement + GetFuturesPositions(context.Context, asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) GetWebsocket() (*stream.Websocket, error) IsWebsocketEnabled() bool @@ -93,8 +98,6 @@ type IBotExchange interface { GetOrderExecutionLimits(a asset.Item, cp currency.Pair) (*order.Limits, error) CheckOrderExecutionLimits(a asset.Item, cp currency.Pair, price, amount float64, orderType order.Type) error UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error - - CurrencyStateManagement } // CurrencyStateManagement defines functionality for currency state management diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go new file mode 100644 index 00000000000..7a9488c4948 --- /dev/null +++ b/exchanges/order/futures.go @@ -0,0 +1,566 @@ +package order + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +// SetupPositionController creates a position controller +// to track futures orders +func SetupPositionController() *PositionController { + return &PositionController{ + positionTrackerControllers: make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker), + } +} + +// TrackNewOrder sets up the maps to then create a +// multi position tracker which funnels down into the +// position tracker, to then track an order's pnl +func (c *PositionController) TrackNewOrder(d *Detail) error { + if d == nil { + return errNilOrder + } + if !d.AssetType.IsFutures() { + return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, ErrNotFutureAsset) + } + if c == nil { + return common.ErrNilPointer + } + c.m.Lock() + defer c.m.Unlock() + if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)]; !ok { + c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + } + if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType]; !ok { + c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType] = make(map[currency.Pair]*MultiPositionTracker) + } + var err error + mpt, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] + if !ok { + mpt, err = SetupMultiPositionTracker(&MultiPositionTrackerSetup{ + Exchange: strings.ToLower(d.Exchange), + Asset: d.AssetType, + Pair: d.Pair, + Underlying: d.Pair.Base, + }) + if err != nil { + return err + } + c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] = mpt + } + return mpt.TrackNewOrder(d) +} + +// GetPositionsForExchange returns all positions for an +// exchange, asset pair that is stored in the position controller +func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*PositionTracker, error) { + if c == nil { + return nil, common.ErrNilPointer + } + c.m.Lock() + defer c.m.Unlock() + if !item.IsFutures() { + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFutureAsset) + } + exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + if !ok { + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) + } + itemM, ok := exchM[item] + if !ok { + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForAsset) + } + multiPositionTracker, ok := itemM[pair] + if !ok { + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) + } + return multiPositionTracker.GetPositions(), nil +} + +// SetupMultiPositionTracker creates a futures order tracker for a specific exchange +func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPositionTracker, error) { + if setup == nil { + return nil, errNilSetup + } + if setup.Exchange == "" { + return nil, errExchangeNameEmpty + } + if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { + return nil, ErrNotFutureAsset + } + if setup.Pair.IsEmpty() { + return nil, ErrPairIsEmpty + } + if setup.Underlying.IsEmpty() { + return nil, errEmptyUnderlying + } + if setup.ExchangePNLCalculation == nil && setup.UseExchangePNLCalculation { + return nil, errMissingPNLCalculationFunctions + } + return &MultiPositionTracker{ + exchange: strings.ToLower(setup.Exchange), + asset: setup.Asset, + pair: setup.Pair, + underlying: setup.Underlying, + offlinePNLCalculation: setup.OfflineCalculation, + orderPositions: make(map[string]*PositionTracker), + useExchangePNLCalculations: setup.UseExchangePNLCalculation, + exchangePNLCalculation: setup.ExchangePNLCalculation, + }, nil +} + +// GetPositions returns all positions +func (e *MultiPositionTracker) GetPositions() []*PositionTracker { + if e == nil { + return nil + } + e.m.Lock() + defer e.m.Unlock() + return e.positions +} + +// TrackNewOrder upserts an order to the tracker and updates position +// status and exposure. PNL is calculated separately as it requires mark prices +func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { + if e == nil { + return common.ErrNilPointer + } + if d == nil { + return ErrSubmissionIsNil + } + e.m.Lock() + defer e.m.Unlock() + if d.AssetType != e.asset { + return errAssetMismatch + } + if tracker, ok := e.orderPositions[d.ID]; ok { + // this has already been associated + // update the tracker + return tracker.TrackNewOrder(d) + } + if len(e.positions) > 0 { + for i := range e.positions { + if e.positions[i].status == Open && i != len(e.positions)-1 { + return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) + } + } + if e.positions[len(e.positions)-1].status == Open { + err := e.positions[len(e.positions)-1].TrackNewOrder(d) + if err != nil && !errors.Is(err, ErrPositionClosed) { + return err + } + e.orderPositions[d.ID] = e.positions[len(e.positions)-1] + return nil + } + } + setup := &PositionTrackerSetup{ + Pair: d.Pair, + EntryPrice: decimal.NewFromFloat(d.Price), + Underlying: d.Pair.Base, + Asset: d.AssetType, + Side: d.Side, + UseExchangePNLCalculation: e.useExchangePNLCalculations, + } + tracker, err := e.SetupPositionTracker(setup) + if err != nil { + return err + } + e.positions = append(e.positions, tracker) + err = tracker.TrackNewOrder(d) + if err != nil { + return err + } + e.orderPositions[d.ID] = tracker + return nil +} + +// SetupPositionTracker creates a new position tracker to track n futures orders +// until the position(s) are closed +func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { + if e == nil { + return nil, common.ErrNilPointer + } + if e.exchange == "" { + return nil, errExchangeNameEmpty + } + if setup == nil { + return nil, errNilSetup + } + if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { + return nil, ErrNotFutureAsset + } + if setup.Pair.IsEmpty() { + return nil, ErrPairIsEmpty + } + + resp := &PositionTracker{ + exchange: strings.ToLower(e.exchange), + asset: setup.Asset, + contractPair: setup.Pair, + underlyingAsset: setup.Underlying, + status: Open, + entryPrice: setup.EntryPrice, + currentDirection: setup.Side, + openingDirection: setup.Side, + useExchangePNLCalculation: setup.UseExchangePNLCalculation, + offlinePNLCalculation: e.offlinePNLCalculation, + } + if !setup.UseExchangePNLCalculation { + // use position tracker's pnl calculation by default + resp.PNLCalculation = &PNLCalculator{} + } else { + if e.exchangePNLCalculation == nil { + return nil, ErrNilPNLCalculator + } + resp.PNLCalculation = e.exchangePNLCalculation + } + return resp, nil +} + +// GetStats returns a summary of a future position +func (p *PositionTracker) GetStats() PositionStats { + if p == nil { + return PositionStats{} + } + p.m.Lock() + defer p.m.Unlock() + return PositionStats{ + Exchange: p.exchange, + Asset: p.asset, + Pair: p.contractPair, + Underlying: p.underlyingAsset, + Status: p.status, + Orders: append(p.longPositions, p.shortPositions...), + RealisedPNL: p.realisedPNL, + UnrealisedPNL: p.unrealisedPNL, + LatestDirection: p.currentDirection, + OpeningDirection: p.openingDirection, + OpeningPrice: p.entryPrice, + LatestPrice: p.latestPrice, + PNLHistory: p.pnlHistory, + } +} + +// TrackPNLByTime calculates the PNL based on a position tracker's exposure +// and current pricing. Adds the entry to PNL history to track over time +func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) error { + if p == nil { + return common.ErrNilPointer + } + p.m.Lock() + defer func() { + p.latestPrice = decimal.NewFromFloat(currentPrice) + p.m.Unlock() + }() + price := decimal.NewFromFloat(currentPrice) + result := &PNLResult{ + Time: t, + Price: price, + } + diff := price.Sub(p.entryPrice) + result.UnrealisedPNL = p.exposure.Mul(diff) + result.Price = price + if len(p.pnlHistory) > 0 { + result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees + result.Exposure = p.pnlHistory[len(p.pnlHistory)-1].Exposure + } + var err error + p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) + return err +} + +// GetRealisedPNL returns the realised pnl if the order +// is closed +func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { + if p == nil { + return decimal.Zero + } + p.m.Lock() + defer p.m.Unlock() + return calculateRealisedPNL(p.pnlHistory) +} + +// GetLatestPNLSnapshot takes the latest pnl history value +// and returns it +func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { + if len(p.pnlHistory) == 0 { + return PNLResult{}, fmt.Errorf("%v %v %v %w", p.exchange, p.asset, p.contractPair, errNoPNLHistory) + } + return p.pnlHistory[len(p.pnlHistory)-1], nil +} + +// TrackNewOrder knows how things are going for a given +// futures contract +func (p *PositionTracker) TrackNewOrder(d *Detail) error { + if p == nil { + return common.ErrNilPointer + } + p.m.Lock() + defer p.m.Unlock() + if p.status == Closed { + return ErrPositionClosed + } + if d == nil { + return ErrSubmissionIsNil + } + if !p.contractPair.Equal(d.Pair) { + return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.contractPair) + } + if p.exchange != strings.ToLower(d.Exchange) { + return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.exchange) + } + if p.asset != d.AssetType { + return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.asset) + } + if d.Side == "" { + return ErrSideIsInvalid + } + if d.ID == "" { + return ErrOrderIDNotSet + } + if d.Date.IsZero() { + return fmt.Errorf("%w for %v %v %v order ID: %v unset", errTimeUnset, d.Exchange, d.AssetType, d.Pair, d.ID) + } + if len(p.shortPositions) == 0 && len(p.longPositions) == 0 { + p.entryPrice = decimal.NewFromFloat(d.Price) + } + + for i := range p.shortPositions { + if p.shortPositions[i].ID == d.ID { + ord := p.shortPositions[i].Copy() + ord.UpdateOrderFromDetail(d) + p.shortPositions[i] = ord + break + } + } + for i := range p.longPositions { + if p.longPositions[i].ID == d.ID { + ord := p.longPositions[i].Copy() + ord.UpdateOrderFromDetail(d) + p.longPositions[i] = ord + break + } + } + + if d.Side.IsShort() { + p.shortPositions = append(p.shortPositions, d.Copy()) + } else { + p.longPositions = append(p.longPositions, d.Copy()) + } + var shortSide, longSide, averageLeverage decimal.Decimal + + for i := range p.shortPositions { + shortSide = shortSide.Add(decimal.NewFromFloat(p.shortPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(p.shortPositions[i].Leverage) + } + for i := range p.longPositions { + longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(p.longPositions[i].Leverage) + } + + averageLeverage.Div(decimal.NewFromInt(int64(len(p.shortPositions))).Add(decimal.NewFromInt(int64(len(p.longPositions))))) + if p.currentDirection == "" { + p.currentDirection = d.Side + } + + var result *PNLResult + var err error + var price, amount, leverage decimal.Decimal + price = decimal.NewFromFloat(d.Price) + amount = decimal.NewFromFloat(d.Amount) + leverage = decimal.NewFromFloat(d.Leverage) + cal := &PNLCalculatorRequest{ + Underlying: p.underlyingAsset, + Asset: p.asset, + OrderDirection: d.Side, + Leverage: leverage, + EntryPrice: p.entryPrice, + Amount: amount, + CurrentPrice: price, + Pair: p.contractPair, + Time: d.Date, + OpeningDirection: p.openingDirection, + CurrentDirection: p.currentDirection, + PNLHistory: p.pnlHistory, + Exposure: p.exposure, + Fee: decimal.NewFromFloat(d.Fee), + CalculateOffline: p.offlinePNLCalculation, + } + if len(p.pnlHistory) != 0 { + cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price + } + if (cal.OrderDirection.IsShort() && cal.CurrentDirection.IsLong() || cal.OrderDirection.IsLong() && cal.CurrentDirection.IsShort()) && + cal.Exposure.LessThan(amount) { + // latest order swaps directions! + // split the order to calculate PNL from each direction + first := amount.Sub(cal.Exposure) + second := cal.Exposure.Sub(amount).Abs() + cal.Fee = cal.Fee.Div(decimal.NewFromInt(2)) + cal.Amount = first + result, err = p.PNLCalculation.CalculatePNL(cal) + if err != nil { + return err + } + p.pnlHistory, err = upsertPNLEntry(cal.PNLHistory, result) + if err != nil { + return err + } + if cal.OrderDirection.IsLong() { + cal.OrderDirection = Short + } else if cal.OrderDirection.IsShort() { + cal.OrderDirection = Long + } + if p.openingDirection.IsLong() { + p.openingDirection = Short + } else if p.openingDirection.IsShort() { + p.openingDirection = Long + } + + cal.Amount = second + cal.EntryPrice = price + cal.Time = cal.Time.Add(1) + cal.PNLHistory = p.pnlHistory + result, err = p.PNLCalculation.CalculatePNL(cal) + + } else { + result, err = p.PNLCalculation.CalculatePNL(cal) + } + if err != nil { + if !errors.Is(err, ErrPositionLiquidated) { + return err + } + result.UnrealisedPNL = decimal.Zero + result.RealisedPNLBeforeFees = decimal.Zero + p.status = Closed + } + p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) + if err != nil { + return err + } + p.unrealisedPNL = result.UnrealisedPNL + + if longSide.GreaterThan(shortSide) { + p.currentDirection = Long + } else if shortSide.GreaterThan(longSide) { + p.currentDirection = Short + } else { + p.currentDirection = UnknownSide + } + if p.currentDirection.IsLong() { + p.exposure = longSide.Sub(shortSide) + } else { + p.exposure = shortSide.Sub(longSide) + } + if p.exposure.Equal(decimal.Zero) { + p.status = Closed + p.closingPrice = decimal.NewFromFloat(d.Price) + p.realisedPNL = calculateRealisedPNL(p.pnlHistory) + p.unrealisedPNL = decimal.Zero + } else if p.exposure.IsNegative() { + if p.currentDirection.IsLong() { + p.currentDirection = Short + } else { + p.currentDirection = Long + } + p.exposure = p.exposure.Abs() + } + return nil +} + +// CalculatePNL this is a localised generic way of calculating open +// positions' worth, it is an implementation of the PNLCalculation interface +// +// do not use any properties of p, use calc, otherwise there will be +// sync issues +func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, error) { + if calc == nil { + return nil, ErrNilPNLCalculator + } + var previousPNL *PNLResult + if len(calc.PNLHistory) > 0 { + previousPNL = &calc.PNLHistory[len(calc.PNLHistory)-1] + } + var prevExposure decimal.Decimal + if previousPNL != nil { + prevExposure = previousPNL.Exposure + } + var currentExposure, realisedPNL, unrealisedPNL, first, second decimal.Decimal + if calc.OpeningDirection.IsLong() { + first = calc.CurrentPrice + if previousPNL != nil { + second = previousPNL.Price + } + } else if calc.OpeningDirection.IsShort() { + if previousPNL != nil { + first = previousPNL.Price + } + second = calc.CurrentPrice + } + switch { + case calc.OpeningDirection.IsShort() && calc.OrderDirection.IsShort(), + calc.OpeningDirection.IsLong() && calc.OrderDirection.IsLong(): + // appending to your position + currentExposure = prevExposure.Add(calc.Amount) + unrealisedPNL = currentExposure.Mul(first.Sub(second)) + case calc.OpeningDirection.IsShort() && calc.OrderDirection.IsLong(), + calc.OpeningDirection.IsLong() && calc.OrderDirection.IsShort(): + // selling/closing your position by "amount" + currentExposure = prevExposure.Sub(calc.Amount) + unrealisedPNL = currentExposure.Mul(first.Sub(second)) + realisedPNL = calc.Amount.Mul(first.Sub(second)) + default: + return nil, fmt.Errorf("%w openinig direction: '%v' order direction: '%v' exposure: '%v'", errCannotCalculateUnrealisedPNL, calc.OpeningDirection, calc.OrderDirection, currentExposure) + } + totalFees := calc.Fee + for i := range calc.PNLHistory { + totalFees = totalFees.Add(calc.PNLHistory[i].Fee) + } + if !unrealisedPNL.IsZero() { + unrealisedPNL = unrealisedPNL.Sub(totalFees) + } + + response := &PNLResult{ + Time: calc.Time, + UnrealisedPNL: unrealisedPNL, + RealisedPNLBeforeFees: realisedPNL, + Price: calc.CurrentPrice, + Exposure: currentExposure, + Fee: calc.Fee, + } + return response, nil +} + +func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { + var realisedPNL, totalFees decimal.Decimal + for i := range pnlHistory { + realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) + totalFees = totalFees.Add(pnlHistory[i].Fee) + } + if realisedPNL.IsZero() { + return decimal.Zero + } + return realisedPNL.Sub(totalFees) +} + +// upsertPNLEntry upserts an entry to PNLHistory field +// with some basic checks +func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, error) { + if entry.Time.IsZero() { + return nil, errTimeUnset + } + for i := range pnlHistory { + if entry.Time.Equal(pnlHistory[i].Time) { + pnlHistory[i] = *entry + return pnlHistory, nil + } + } + pnlHistory = append(pnlHistory, *entry) + return pnlHistory, nil +} diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go new file mode 100644 index 00000000000..4bc0d815ac4 --- /dev/null +++ b/exchanges/order/futures_test.go @@ -0,0 +1,667 @@ +package order + +import ( + "errors" + "testing" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +type FakePNL struct { + err error + result *PNLResult +} + +func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { + if f.err != nil { + return nil, f.err + } + return f.result, nil +} + +func TestUpsertPNLEntry(t *testing.T) { + t.Parallel() + var results []PNLResult + result := &PNLResult{} + _, err := upsertPNLEntry(results, result) + if !errors.Is(err, errTimeUnset) { + t.Error(err) + } + tt := time.Now() + result.Time = tt + results, err = upsertPNLEntry(results, result) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(results) != 1 { + t.Errorf("expected 1 received %v", len(results)) + } + result.Fee = decimal.NewFromInt(1337) + results, err = upsertPNLEntry(results, result) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(results) != 1 { + t.Errorf("expected 1 received %v", len(results)) + } + if !results[0].Fee.Equal(result.Fee) { + t.Errorf("expected %v received %v", result.Fee, results[0].Fee) + } +} + +func TestTrackNewOrder(t *testing.T) { + t.Parallel() + exch := "test" + item := asset.Futures + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Error(err) + } + e := MultiPositionTracker{ + exchange: "test", + exchangePNLCalculation: &FakePNL{}, + } + setup := &PositionTrackerSetup{ + Pair: pair, + Asset: item, + } + f, err := e.SetupPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + + err = f.TrackNewOrder(nil) + if !errors.Is(err, ErrSubmissionIsNil) { + t.Error(err) + } + err = f.TrackNewOrder(&Detail{}) + if !errors.Is(err, errOrderNotEqualToTracker) { + t.Error(err) + } + + od := &Detail{ + Exchange: exch, + AssetType: item, + Pair: pair, + ID: "1", + Price: 1337, + } + err = f.TrackNewOrder(od) + if !errors.Is(err, ErrSideIsInvalid) { + t.Error(err) + } + + od.Side = Long + od.Amount = 1 + od.ID = "2" + err = f.TrackNewOrder(od) + if !errors.Is(err, errTimeUnset) { + t.Error(err) + } + f.openingDirection = Long + od.Date = time.Now() + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if !f.entryPrice.Equal(decimal.NewFromInt(1337)) { + t.Errorf("expected 1337, received %v", f.entryPrice) + } + if len(f.longPositions) != 1 { + t.Error("expected a long") + } + if f.currentDirection != Long { + t.Error("expected recognition that its long") + } + if f.exposure.InexactFloat64() != od.Amount { + t.Error("expected 1") + } + + od.Amount = 0.4 + od.Side = Short + od.ID = "3" + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(f.shortPositions) != 1 { + t.Error("expected a short") + } + if f.currentDirection != Long { + t.Error("expected recognition that its long") + } + if f.exposure.InexactFloat64() != 0.6 { + t.Error("expected 0.6") + } + od.Amount = 0.8 + od.Side = Short + od.ID = "4" + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if f.currentDirection != Short { + t.Error("expected recognition that its short") + } + if !f.exposure.Equal(decimal.NewFromFloat(0.2)) { + t.Errorf("expected %v received %v", 0.2, f.exposure) + } + + od.ID = "5" + od.Side = Long + od.Amount = 0.2 + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if f.currentDirection != UnknownSide { + t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) + } + if f.status != Closed { + t.Errorf("expected recognition that its closed, received '%v'", f.status) + } + + err = f.TrackNewOrder(od) + if !errors.Is(err, ErrPositionClosed) { + t.Error(err) + } + if f.currentDirection != UnknownSide { + t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) + } + if f.status != Closed { + t.Errorf("expected recognition that its closed, received '%v'", f.status) + } +} + +func TestSetupMultiPositionTracker(t *testing.T) { + t.Parallel() + + _, err := SetupMultiPositionTracker(nil) + if !errors.Is(err, errNilSetup) { + t.Error(err) + } + + setup := &MultiPositionTrackerSetup{} + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, errExchangeNameEmpty) { + t.Error(err) + } + setup.Exchange = "test" + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, ErrNotFutureAsset) { + t.Error(err) + } + setup.Asset = asset.Futures + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, ErrPairIsEmpty) { + t.Error(err) + } + + setup.Pair = currency.NewPair(currency.BTC, currency.USDT) + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, errEmptyUnderlying) { + t.Error(err) + } + + setup.Underlying = currency.BTC + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + + setup.UseExchangePNLCalculation = true + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, errMissingPNLCalculationFunctions) { + t.Error(err) + } + + setup.ExchangePNLCalculation = &FakePNL{} + resp, err := SetupMultiPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + if resp.exchange != "test" { + t.Errorf("expected 'test' received %v", resp.exchange) + } +} + +func TestExchangeTrackNewOrder(t *testing.T) { + t.Parallel() + exch := "test" + item := asset.Futures + pair := currency.NewPair(currency.BTC, currency.USDT) + setup := &MultiPositionTrackerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + ExchangePNLCalculation: &FakePNL{}, + } + resp, err := SetupMultiPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + + tt := time.Now() + + err = resp.TrackNewOrder(&Detail{ + Date: tt, + Exchange: exch, + AssetType: item, + Pair: pair, + Side: Short, + ID: "1", + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(resp.positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.positions)) + } + + err = resp.TrackNewOrder(&Detail{ + Date: tt, + Exchange: exch, + AssetType: item, + Pair: pair, + Side: Short, + ID: "1", + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(resp.positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.positions)) + } + + err = resp.TrackNewOrder(&Detail{ + Date: tt, + Exchange: exch, + AssetType: item, + Pair: pair, + Side: Long, + ID: "2", + Amount: 2, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(resp.positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.positions)) + } + if resp.positions[0].status != Closed { + t.Errorf("expected 'closed' received %v", resp.positions[0].status) + } + resp.positions[0].status = Open + resp.positions = append(resp.positions, resp.positions...) + err = resp.TrackNewOrder(&Detail{ + Date: tt, + Exchange: exch, + AssetType: item, + Pair: pair, + Side: Long, + ID: "2", + Amount: 2, + }) + if len(resp.positions) != 2 { + t.Errorf("expected '2' received %v", len(resp.positions)) + } + + resp.positions[0].status = Closed + err = resp.TrackNewOrder(&Detail{ + Date: tt, + Exchange: exch, + Pair: pair, + AssetType: asset.USDTMarginedFutures, + Side: Long, + ID: "2", + Amount: 2, + }) + if !errors.Is(err, errAssetMismatch) { + t.Error(err) + } +} + +func TestSetupPositionControllerReal(t *testing.T) { + t.Parallel() + pc := SetupPositionController() + if pc.positionTrackerControllers == nil { + t.Error("unexpected nil") + } +} + +func TestPositionControllerTestTrackNewOrder(t *testing.T) { + t.Parallel() + pc := SetupPositionController() + err := pc.TrackNewOrder(nil) + if !errors.Is(err, errNilOrder) { + t.Error(err) + } + + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Spot, + Side: Long, + ID: "lol", + }) + if !errors.Is(err, ErrNotFutureAsset) { + t.Error(err) + } + + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + Side: Long, + ID: "lol", + }) + if !errors.Is(err, nil) { + t.Error(err) + } +} + +func TestGetLatestPNLSnapshot(t *testing.T) { + t.Parallel() + pt := PositionTracker{} + _, err := pt.GetLatestPNLSnapshot() + if !errors.Is(err, errNoPNLHistory) { + t.Error(err) + } + + pnl := PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1337), + } + pt.pnlHistory = append(pt.pnlHistory, pnl) + + result, err := pt.GetLatestPNLSnapshot() + if !errors.Is(err, nil) { + t.Error(err) + } + if result != pt.pnlHistory[0] { + t.Error("unexpected result") + } +} + +func TestGetRealisedPNL(t *testing.T) { + t.Parallel() + p := PositionTracker{} + result := p.GetRealisedPNL() + if !result.IsZero() { + t.Error("expected zero") + } +} + +func TestGetStats(t *testing.T) { + t.Parallel() + + p := &PositionTracker{} + stats := p.GetStats() + if len(stats.Orders) != 0 { + t.Error("expected 0") + } + + p.exchange = "test" + stats = p.GetStats() + if stats.Exchange != p.exchange { + t.Errorf("expected '%v' received '%v'", p.exchange, stats.Exchange) + } + + p = nil + stats = p.GetStats() + if len(stats.Orders) != 0 { + t.Error("expected 0") + } +} + +func TestGetPositions(t *testing.T) { + t.Parallel() + p := &MultiPositionTracker{} + positions := p.GetPositions() + if len(positions) > 0 { + t.Error("expected 0") + } + + p.positions = append(p.positions, &PositionTracker{ + exchange: "test", + }) + positions = p.GetPositions() + if len(positions) != 1 { + t.Fatal("expected 1") + } + if positions[0].exchange != "test" { + t.Error("expected 'test'") + } + + p = nil + positions = p.GetPositions() + if len(positions) > 0 { + t.Error("expected 0") + } + +} + +func TestGetPositionsForExchange(t *testing.T) { + t.Parallel() + c := &PositionController{} + p := currency.NewPair(currency.BTC, currency.USDT) + pos, err := c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + if len(pos) != 0 { + t.Error("expected zero") + } + c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers["test"] = nil + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForAsset) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + c.positionTrackerControllers["test"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers["test"][asset.Futures] = nil + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForPair) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) + } + pos, err = c.GetPositionsForExchange("test", asset.Spot, p) + if !errors.Is(err, ErrNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + } + + c.positionTrackerControllers["test"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ + exchange: "test", + } + + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if len(pos) != 0 { + t.Fatal("expected zero") + } + c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ + exchange: "test", + positions: []*PositionTracker{ + { + exchange: "test", + }, + }, + } + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if len(pos) != 1 { + t.Fatal("expected 1") + } + if pos[0].exchange != "test" { + t.Error("expected test") + } + c = nil + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } +} + +func TestCalculateRealisedPNL(t *testing.T) { + t.Parallel() + result := calculateRealisedPNL(nil) + if !result.IsZero() { + t.Error("expected zero") + } + result = calculateRealisedPNL([]PNLResult{ + { + RealisedPNLBeforeFees: decimal.NewFromInt(1337), + }, + }) + if !result.Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") + } + + result = calculateRealisedPNL([]PNLResult{ + { + RealisedPNLBeforeFees: decimal.NewFromInt(1339), + Fee: decimal.NewFromInt(2), + }, + { + RealisedPNLBeforeFees: decimal.NewFromInt(2), + Fee: decimal.NewFromInt(2), + }, + }) + if !result.Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") + } +} + +func TestSetupPositionTracker(t *testing.T) { + t.Parallel() + m := &MultiPositionTracker{} + p, err := m.SetupPositionTracker(nil) + if !errors.Is(err, errExchangeNameEmpty) { + t.Errorf("received '%v' expected '%v", err, errExchangeNameEmpty) + } + if p != nil { + t.Error("expected nil") + } + m.exchange = "test" + p, err = m.SetupPositionTracker(nil) + if !errors.Is(err, errNilSetup) { + t.Errorf("received '%v' expected '%v", err, errNilSetup) + } + if p != nil { + t.Error("expected nil") + } + + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Spot, + }) + if !errors.Is(err, ErrNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + } + if p != nil { + t.Error("expected nil") + } + + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + }) + if !errors.Is(err, ErrPairIsEmpty) { + t.Errorf("received '%v' expected '%v", err, ErrPairIsEmpty) + } + if p != nil { + t.Error("expected nil") + } + + cp := currency.NewPair(currency.BTC, currency.USDT) + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + Pair: cp, + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if p == nil { + t.Error("expected nil") + } + if p.exchange != "test" { + t.Error("expected test") + } + + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + Pair: cp, + UseExchangePNLCalculation: true, + }) + if !errors.Is(err, ErrNilPNLCalculator) { + t.Errorf("received '%v' expected '%v", err, ErrNilPNLCalculator) + } + m.exchangePNLCalculation = &PNLCalculator{} + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + Pair: cp, + UseExchangePNLCalculation: true, + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if !p.useExchangePNLCalculation { + t.Error("expected true") + } +} + +func TestCalculatePNL(t *testing.T) { + t.Parallel() + p := &PNLCalculator{} + _, err := p.CalculatePNL(nil) + if !errors.Is(err, ErrNilPNLCalculator) { + t.Errorf("received '%v' expected '%v", err, ErrNilPNLCalculator) + } + _, err = p.CalculatePNL(&PNLCalculatorRequest{}) + if !errors.Is(err, errCannotCalculateUnrealisedPNL) { + t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) + } + + _, err = p.CalculatePNL(&PNLCalculatorRequest{ + OrderDirection: Short, + CurrentDirection: Long, + }) + if !errors.Is(err, errCannotCalculateUnrealisedPNL) { + t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) + } + +} + +func TestTrackPNLByTime(t *testing.T) { + t.Parallel() + p := &PositionTracker{} + err := p.TrackPNLByTime(time.Now(), 1) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + + err = p.TrackPNLByTime(time.Now(), 2) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if !p.latestPrice.Equal(decimal.NewFromInt(2)) { + t.Error("expected 2") + } + p = nil + err = p.TrackPNLByTime(time.Now(), 2) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } +} diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go new file mode 100644 index 00000000000..4052dca6576 --- /dev/null +++ b/exchanges/order/futures_types.go @@ -0,0 +1,228 @@ +package order + +import ( + "context" + "errors" + "sync" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +var ( + // ErrPositionClosed returned when attempting to amend a closed position + ErrPositionClosed = errors.New("the position is closed") + // ErrPositionsNotLoadedForExchange returned when no position data exists for an exchange + ErrPositionsNotLoadedForExchange = errors.New("no positions loaded for exchange") + // ErrPositionsNotLoadedForAsset returned when no position data exists for an asset + ErrPositionsNotLoadedForAsset = errors.New("no positions loaded for asset") + // ErrPositionsNotLoadedForPair returned when no position data exists for a pair + ErrPositionsNotLoadedForPair = errors.New("no positions loaded for pair") + // ErrNilPNLCalculator is raised when pnl calculation is requested for + // an exchange, but the fields are not set properly + ErrNilPNLCalculator = errors.New("nil pnl calculator received") + // ErrPositionLiquidated is raised when checking PNL status only for + // it to be liquidated + ErrPositionLiquidated = errors.New("position liquidated") + // ErrNotFutureAsset returned when futures data is requested on a non-futures asset + ErrNotFutureAsset = errors.New("asset type is not futures") + + errExchangeNameEmpty = errors.New("exchange name empty") + errTimeUnset = errors.New("time unset") + errMissingPNLCalculationFunctions = errors.New("futures tracker requires exchange PNL calculation functions") + errOrderNotEqualToTracker = errors.New("order does not match tracker data") + errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") + errAssetMismatch = errors.New("provided asset does not match") + errEmptyUnderlying = errors.New("underlying asset unset") + errNilSetup = errors.New("nil setup received") + errNilOrder = errors.New("nil order received") + errNoPNLHistory = errors.New("no pnl history") + errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL") +) + +// PNLCalculation is an interface to allow multiple +// ways of calculating PNL to be used for futures positions +type PNLCalculation interface { + CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) +} + +// CollateralManagement is an interface that allows +// multiple ways of calculating the size of collateral +// on an exchange +type CollateralManagement interface { + ScaleCollateral(context.Context, *CollateralCalculator) (decimal.Decimal, error) + CalculateTotalCollateral(context.Context, []CollateralCalculator) (*TotalCollateralResponse, error) +} + +// TotalCollateralResponse holds all collateral +type TotalCollateralResponse struct { + TotalCollateral decimal.Decimal + BreakdownByCurrency []CollateralByCurrency +} + +// CollateralByCurrency individual collateral contribution +// along with what the potentially scaled collateral +// currency it is represented as +// eg in FTX ValueCurrency is USD +type CollateralByCurrency struct { + Currency currency.Code + Amount decimal.Decimal + ValueCurrency currency.Code +} + +// PositionController manages all futures orders +// across all exchanges assets and pairs +// its purpose is to handle the minutia of tracking +// and so all you need to do is send all orders to +// the position controller and its all tracked happily +type PositionController struct { + m sync.Mutex + positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker +} + +// MultiPositionTracker will track the performance of +// futures positions over time. If an old position tracker +// is closed, then the position controller will create a new one +// to track the current positions +type MultiPositionTracker struct { + m sync.Mutex + exchange string + asset asset.Item + pair currency.Pair + underlying currency.Code + positions []*PositionTracker + // order positions allows for an easier time knowing which order is + // part of which position tracker + orderPositions map[string]*PositionTracker + pnl decimal.Decimal + offlinePNLCalculation bool + useExchangePNLCalculations bool + exchangePNLCalculation PNLCalculation +} + +// MultiPositionTrackerSetup holds the parameters +// required to set up a multi position tracker +type MultiPositionTrackerSetup struct { + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + OfflineCalculation bool + UseExchangePNLCalculation bool + ExchangePNLCalculation PNLCalculation +} + +// PositionTracker tracks futures orders until the overall position is considered closed +// eg a user can open a short position, append to it via two more shorts, reduce via a small long and +// finally close off the remainder via another long. All of these actions are to be +// captured within one position tracker. It allows for a user to understand their PNL +// specifically for futures positions. Utilising spot/futures arbitrage will not be tracked +// completely within this position tracker, however, can still provide a good +// timeline of performance until the position is closed +type PositionTracker struct { + m sync.Mutex + exchange string + asset asset.Item + contractPair currency.Pair + underlyingAsset currency.Code + exposure decimal.Decimal + currentDirection Side + openingDirection Side + status Status + averageLeverage decimal.Decimal + unrealisedPNL decimal.Decimal + realisedPNL decimal.Decimal + shortPositions []Detail + longPositions []Detail + pnlHistory []PNLResult + entryPrice decimal.Decimal + closingPrice decimal.Decimal + offlinePNLCalculation bool + PNLCalculation + latestPrice decimal.Decimal + useExchangePNLCalculation bool +} + +// PositionTrackerSetup contains all required fields to +// setup a position tracker +type PositionTrackerSetup struct { + Pair currency.Pair + EntryPrice decimal.Decimal + Underlying currency.Code + Asset asset.Item + Side Side + UseExchangePNLCalculation bool +} + +// CollateralCalculator is used to determine +// the size of collateral holdings for an exchange +// eg on FTX, the collateral is scaled depending on what +// currency it is +type CollateralCalculator struct { + CalculateOffline bool + CollateralCurrency currency.Code + Asset asset.Item + Side Side + CollateralAmount decimal.Decimal + USDPrice decimal.Decimal + IsLiquidating bool +} + +// PNLCalculator implements the PNLCalculation interface +// to call CalculatePNL and is used when a user wishes to have a +// consistent method of calculating PNL across different exchanges +type PNLCalculator struct{} + +// PNLCalculatorRequest is used to calculate PNL values +// for an open position +type PNLCalculatorRequest struct { + Pair currency.Pair + CalculateOffline bool + Underlying currency.Code + Asset asset.Item + Leverage decimal.Decimal + EntryPrice decimal.Decimal + EntryAmount decimal.Decimal + Amount decimal.Decimal + CurrentPrice decimal.Decimal + PreviousPrice decimal.Decimal + Time time.Time + OrderID string + Fee decimal.Decimal + PNLHistory []PNLResult + Exposure decimal.Decimal + OrderDirection Side + OpeningDirection Side + CurrentDirection Side +} + +// PNLResult stores a PNL result from a point in time +type PNLResult struct { + Time time.Time + UnrealisedPNL decimal.Decimal + RealisedPNLBeforeFees decimal.Decimal + Price decimal.Decimal + Exposure decimal.Decimal + Fee decimal.Decimal + IsLiquidated bool +} + +// PositionStats is a basic holder +// for position information +type PositionStats struct { + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + Orders []Detail + RealisedPNL decimal.Decimal + UnrealisedPNL decimal.Decimal + LatestDirection Side + Status Status + OpeningDirection Side + OpeningPrice decimal.Decimal + LatestPrice decimal.Decimal + PNLHistory []PNLResult +} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 0d395bd864e..5ef124dbe90 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -295,6 +295,8 @@ const ( Bid Side = "BID" Ask Side = "ASK" UnknownSide Side = "UNKNOWN" + Long Side = "LONG" + Short Side = "SHORT" ) // ByPrice used for sorting orders by price diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index c3519379fbd..94a232e9a5b 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -483,6 +483,16 @@ func (s Side) Title() string { return strings.Title(strings.ToLower(string(s))) } +// IsShort returns if the side is short +func (s Side) IsShort() bool { + return s == Short || s == Sell +} + +// IsLong returns if the side is long +func (s Side) IsLong() bool { + return s == Long || s == Buy +} + // String implements the stringer interface func (s Status) String() string { return string(s) @@ -714,6 +724,10 @@ func StringToOrderSide(side string) (Side, error) { return Bid, nil case strings.EqualFold(side, Ask.String()): return Ask, nil + case strings.EqualFold(side, Long.String()): + return Long, nil + case strings.EqualFold(side, Short.String()): + return Short, nil case strings.EqualFold(side, AnySide.String()): return AnySide, nil default: diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 5f451c79660..7ef71ba2121 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc v3.15.1 // source: rpc.proto package gctrpc @@ -10765,6 +10765,480 @@ func (x *CurrencyState) GetTradingEnabled() bool { return false } +type GetFuturesPositionsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,3,opt,name=pair,proto3" json:"pair,omitempty"` + StartDate string `protobuf:"bytes,4,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` + EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` + Verbose bool `protobuf:"varint,8,opt,name=verbose,proto3" json:"verbose,omitempty"` +} + +func (x *GetFuturesPositionsRequest) Reset() { + *x = GetFuturesPositionsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[169] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFuturesPositionsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFuturesPositionsRequest) ProtoMessage() {} + +func (x *GetFuturesPositionsRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[169] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFuturesPositionsRequest.ProtoReflect.Descriptor instead. +func (*GetFuturesPositionsRequest) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{169} +} + +func (x *GetFuturesPositionsRequest) GetExchange() string { + if x != nil { + return x.Exchange + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetAsset() string { + if x != nil { + return x.Asset + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetPair() *CurrencyPair { + if x != nil { + return x.Pair + } + return nil +} + +func (x *GetFuturesPositionsRequest) GetStartDate() string { + if x != nil { + return x.StartDate + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetEndDate() string { + if x != nil { + return x.EndDate + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetPositionLimit() int64 { + if x != nil { + return x.PositionLimit + } + return 0 +} + +func (x *GetFuturesPositionsRequest) GetVerbose() bool { + if x != nil { + return x.Verbose + } + return false +} + +type GetFuturesPositionsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` + TotalRealisedPNL float64 `protobuf:"fixed64,2,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` + TotalUnrealisedPNL float64 `protobuf:"fixed64,3,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` + TotalPNL float64 `protobuf:"fixed64,4,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` + Positions []*FuturePosition `protobuf:"bytes,5,rep,name=positions,proto3" json:"positions,omitempty"` +} + +func (x *GetFuturesPositionsResponse) Reset() { + *x = GetFuturesPositionsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[170] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFuturesPositionsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFuturesPositionsResponse) ProtoMessage() {} + +func (x *GetFuturesPositionsResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[170] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFuturesPositionsResponse.ProtoReflect.Descriptor instead. +func (*GetFuturesPositionsResponse) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{170} +} + +func (x *GetFuturesPositionsResponse) GetTotalOrders() int64 { + if x != nil { + return x.TotalOrders + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetTotalRealisedPNL() float64 { + if x != nil { + return x.TotalRealisedPNL + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetTotalUnrealisedPNL() float64 { + if x != nil { + return x.TotalUnrealisedPNL + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetTotalPNL() float64 { + if x != nil { + return x.TotalPNL + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetPositions() []*FuturePosition { + if x != nil { + return x.Positions + } + return nil +} + +type FuturePosition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + CurrentDirection string `protobuf:"bytes,2,opt,name=currentDirection,proto3" json:"currentDirection,omitempty"` + UnrealisedPNL string `protobuf:"bytes,3,opt,name=unrealisedPNL,proto3" json:"unrealisedPNL,omitempty"` + RealisedPNL string `protobuf:"bytes,4,opt,name=realisedPNL,proto3" json:"realisedPNL,omitempty"` + OpeningDate string `protobuf:"bytes,5,opt,name=openingDate,proto3" json:"openingDate,omitempty"` + ClosingDate string `protobuf:"bytes,6,opt,name=closingDate,proto3" json:"closingDate,omitempty"` + Orders []*OrderDetails `protobuf:"bytes,7,rep,name=orders,proto3" json:"orders,omitempty"` +} + +func (x *FuturePosition) Reset() { + *x = FuturePosition{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[171] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FuturePosition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FuturePosition) ProtoMessage() {} + +func (x *FuturePosition) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[171] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FuturePosition.ProtoReflect.Descriptor instead. +func (*FuturePosition) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{171} +} + +func (x *FuturePosition) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *FuturePosition) GetCurrentDirection() string { + if x != nil { + return x.CurrentDirection + } + return "" +} + +func (x *FuturePosition) GetUnrealisedPNL() string { + if x != nil { + return x.UnrealisedPNL + } + return "" +} + +func (x *FuturePosition) GetRealisedPNL() string { + if x != nil { + return x.RealisedPNL + } + return "" +} + +func (x *FuturePosition) GetOpeningDate() string { + if x != nil { + return x.OpeningDate + } + return "" +} + +func (x *FuturePosition) GetClosingDate() string { + if x != nil { + return x.ClosingDate + } + return "" +} + +func (x *FuturePosition) GetOrders() []*OrderDetails { + if x != nil { + return x.Orders + } + return nil +} + +type GetCollateralRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + SubAccount string `protobuf:"bytes,3,opt,name=subAccount,proto3" json:"subAccount,omitempty"` + IncludeBreakdown bool `protobuf:"varint,4,opt,name=includeBreakdown,proto3" json:"includeBreakdown,omitempty"` + CalculateOffline bool `protobuf:"varint,5,opt,name=calculateOffline,proto3" json:"calculateOffline,omitempty"` +} + +func (x *GetCollateralRequest) Reset() { + *x = GetCollateralRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[172] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCollateralRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCollateralRequest) ProtoMessage() {} + +func (x *GetCollateralRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[172] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCollateralRequest.ProtoReflect.Descriptor instead. +func (*GetCollateralRequest) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{172} +} + +func (x *GetCollateralRequest) GetExchange() string { + if x != nil { + return x.Exchange + } + return "" +} + +func (x *GetCollateralRequest) GetAsset() string { + if x != nil { + return x.Asset + } + return "" +} + +func (x *GetCollateralRequest) GetSubAccount() string { + if x != nil { + return x.SubAccount + } + return "" +} + +func (x *GetCollateralRequest) GetIncludeBreakdown() bool { + if x != nil { + return x.IncludeBreakdown + } + return false +} + +func (x *GetCollateralRequest) GetCalculateOffline() bool { + if x != nil { + return x.CalculateOffline + } + return false +} + +type GetCollateralResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TotalCollateral string `protobuf:"bytes,1,opt,name=totalCollateral,proto3" json:"totalCollateral,omitempty"` + CurrencyBreakdown []*CollateralForCurrency `protobuf:"bytes,2,rep,name=currencyBreakdown,proto3" json:"currencyBreakdown,omitempty"` +} + +func (x *GetCollateralResponse) Reset() { + *x = GetCollateralResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[173] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCollateralResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCollateralResponse) ProtoMessage() {} + +func (x *GetCollateralResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[173] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCollateralResponse.ProtoReflect.Descriptor instead. +func (*GetCollateralResponse) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{173} +} + +func (x *GetCollateralResponse) GetTotalCollateral() string { + if x != nil { + return x.TotalCollateral + } + return "" +} + +func (x *GetCollateralResponse) GetCurrencyBreakdown() []*CollateralForCurrency { + if x != nil { + return x.CurrencyBreakdown + } + return nil +} + +type CollateralForCurrency struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` + ScaledCollateral string `protobuf:"bytes,2,opt,name=scaledCollateral,proto3" json:"scaledCollateral,omitempty"` + ScaledToCurrency string `protobuf:"bytes,3,opt,name=scaledToCurrency,proto3" json:"scaledToCurrency,omitempty"` +} + +func (x *CollateralForCurrency) Reset() { + *x = CollateralForCurrency{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[174] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CollateralForCurrency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CollateralForCurrency) ProtoMessage() {} + +func (x *CollateralForCurrency) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[174] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CollateralForCurrency.ProtoReflect.Descriptor instead. +func (*CollateralForCurrency) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{174} +} + +func (x *CollateralForCurrency) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *CollateralForCurrency) GetScaledCollateral() string { + if x != nil { + return x.ScaledCollateral + } + return "" +} + +func (x *CollateralForCurrency) GetScaledToCurrency() string { + if x != nil { + return x.ScaledToCurrency + } + return "" +} + type CancelBatchOrdersResponse_Orders struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -10776,7 +11250,7 @@ type CancelBatchOrdersResponse_Orders struct { func (x *CancelBatchOrdersResponse_Orders) Reset() { *x = CancelBatchOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[179] + mi := &file_rpc_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10789,7 +11263,7 @@ func (x *CancelBatchOrdersResponse_Orders) String() string { func (*CancelBatchOrdersResponse_Orders) ProtoMessage() {} func (x *CancelBatchOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[179] + mi := &file_rpc_proto_msgTypes[185] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10824,7 +11298,7 @@ type CancelAllOrdersResponse_Orders struct { func (x *CancelAllOrdersResponse_Orders) Reset() { *x = CancelAllOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[181] + mi := &file_rpc_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10837,7 +11311,7 @@ func (x *CancelAllOrdersResponse_Orders) String() string { func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} func (x *CancelAllOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[181] + mi := &file_rpc_proto_msgTypes[187] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12305,7 +12779,86 @@ var file_rpc_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x32, 0x99, 0x57, 0x0a, 0x0e, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, + 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, + 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, + 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, + 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, + 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, + 0x4e, 0x4c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x24, 0x0a, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, + 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, + 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, + 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, + 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, + 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, + 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, + 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x12, 0x4b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, + 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, + 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, + 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, + 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, @@ -13003,11 +13556,25 @@ var file_rpc_proto_rawDesc = []byte{ 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, + 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -13022,7 +13589,7 @@ func file_rpc_proto_rawDescGZIP() []byte { return file_rpc_proto_rawDescData } -var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 185) +var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 191) var file_rpc_proto_goTypes = []interface{}{ (*GetInfoRequest)(nil), // 0: gctrpc.GetInfoRequest (*GetInfoResponse)(nil), // 1: gctrpc.GetInfoResponse @@ -13193,32 +13760,38 @@ var file_rpc_proto_goTypes = []interface{}{ (*CurrencyStateDepositRequest)(nil), // 166: gctrpc.CurrencyStateDepositRequest (*CurrencyStateResponse)(nil), // 167: gctrpc.CurrencyStateResponse (*CurrencyState)(nil), // 168: gctrpc.CurrencyState - nil, // 169: gctrpc.GetInfoResponse.SubsystemStatusEntry - nil, // 170: gctrpc.GetInfoResponse.RpcEndpointsEntry - nil, // 171: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - nil, // 172: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - nil, // 173: gctrpc.GetRPCEndpointsResponse.EndpointsEntry - nil, // 174: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - nil, // 175: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - nil, // 176: gctrpc.OnlineCoins.CoinsEntry - nil, // 177: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - nil, // 178: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - (*CancelBatchOrdersResponse_Orders)(nil), // 179: gctrpc.CancelBatchOrdersResponse.Orders - nil, // 180: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - (*CancelAllOrdersResponse_Orders)(nil), // 181: gctrpc.CancelAllOrdersResponse.Orders - nil, // 182: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - nil, // 183: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - nil, // 184: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - (*timestamppb.Timestamp)(nil), // 185: google.protobuf.Timestamp + (*GetFuturesPositionsRequest)(nil), // 169: gctrpc.GetFuturesPositionsRequest + (*GetFuturesPositionsResponse)(nil), // 170: gctrpc.GetFuturesPositionsResponse + (*FuturePosition)(nil), // 171: gctrpc.FuturePosition + (*GetCollateralRequest)(nil), // 172: gctrpc.GetCollateralRequest + (*GetCollateralResponse)(nil), // 173: gctrpc.GetCollateralResponse + (*CollateralForCurrency)(nil), // 174: gctrpc.CollateralForCurrency + nil, // 175: gctrpc.GetInfoResponse.SubsystemStatusEntry + nil, // 176: gctrpc.GetInfoResponse.RpcEndpointsEntry + nil, // 177: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + nil, // 178: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + nil, // 179: gctrpc.GetRPCEndpointsResponse.EndpointsEntry + nil, // 180: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + nil, // 181: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + nil, // 182: gctrpc.OnlineCoins.CoinsEntry + nil, // 183: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + nil, // 184: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + (*CancelBatchOrdersResponse_Orders)(nil), // 185: gctrpc.CancelBatchOrdersResponse.Orders + nil, // 186: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + (*CancelAllOrdersResponse_Orders)(nil), // 187: gctrpc.CancelAllOrdersResponse.Orders + nil, // 188: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + nil, // 189: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + nil, // 190: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + (*timestamppb.Timestamp)(nil), // 191: google.protobuf.Timestamp } var file_rpc_proto_depIdxs = []int32{ - 169, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry - 170, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry - 171, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - 172, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - 173, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry - 174, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - 175, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + 175, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry + 176, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry + 177, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + 178, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + 179, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry + 180, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + 181, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry 21, // 7: gctrpc.GetTickerRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 8: gctrpc.TickerResponse.pair:type_name -> gctrpc.CurrencyPair 22, // 9: gctrpc.Tickers.tickers:type_name -> gctrpc.TickerResponse @@ -13233,12 +13806,12 @@ var file_rpc_proto_depIdxs = []int32{ 33, // 18: gctrpc.GetAccountInfoResponse.accounts:type_name -> gctrpc.Account 38, // 19: gctrpc.GetPortfolioResponse.portfolio:type_name -> gctrpc.PortfolioAddress 43, // 20: gctrpc.OfflineCoins.addresses:type_name -> gctrpc.OfflineCoinSummary - 176, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry + 182, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry 42, // 22: gctrpc.GetPortfolioSummaryResponse.coin_totals:type_name -> gctrpc.Coin 42, // 23: gctrpc.GetPortfolioSummaryResponse.coins_offline:type_name -> gctrpc.Coin - 177, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + 183, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry 42, // 25: gctrpc.GetPortfolioSummaryResponse.coins_online:type_name -> gctrpc.Coin - 178, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + 184, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry 51, // 27: gctrpc.GetForexProvidersResponse.forex_providers:type_name -> gctrpc.ForexProvider 54, // 28: gctrpc.GetForexRatesResponse.forex_rates:type_name -> gctrpc.ForexRatesConversion 57, // 29: gctrpc.OrderDetails.trades:type_name -> gctrpc.TradeHistory @@ -13252,23 +13825,23 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 37: gctrpc.WhaleBombRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 38: gctrpc.CancelOrderRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 39: gctrpc.CancelBatchOrdersRequest.pair:type_name -> gctrpc.CurrencyPair - 179, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders - 181, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders + 185, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders + 187, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders 73, // 42: gctrpc.GetEventsResponse.condition_params:type_name -> gctrpc.ConditionParams 21, // 43: gctrpc.GetEventsResponse.pair:type_name -> gctrpc.CurrencyPair 73, // 44: gctrpc.AddEventRequest.condition_params:type_name -> gctrpc.ConditionParams 21, // 45: gctrpc.AddEventRequest.pair:type_name -> gctrpc.CurrencyPair 79, // 46: gctrpc.DepositAddresses.addresses:type_name -> gctrpc.DepositAddress - 183, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + 189, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry 94, // 48: gctrpc.WithdrawalEventByIDResponse.event:type_name -> gctrpc.WithdrawalEventResponse 94, // 49: gctrpc.WithdrawalEventsByExchangeResponse.event:type_name -> gctrpc.WithdrawalEventResponse 95, // 50: gctrpc.WithdrawalEventResponse.exchange:type_name -> gctrpc.WithdrawlExchangeEvent 96, // 51: gctrpc.WithdrawalEventResponse.request:type_name -> gctrpc.WithdrawalRequestEvent - 185, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp - 185, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp + 191, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp + 191, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp 97, // 54: gctrpc.WithdrawalRequestEvent.fiat:type_name -> gctrpc.FiatWithdrawalEvent 98, // 55: gctrpc.WithdrawalRequestEvent.crypto:type_name -> gctrpc.CryptoWithdrawalEvent - 184, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + 190, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry 21, // 57: gctrpc.SetExchangePairRequest.pairs:type_name -> gctrpc.CurrencyPair 21, // 58: gctrpc.GetOrderbookStreamRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 59: gctrpc.GetTickerStreamRequest.pair:type_name -> gctrpc.CurrencyPair @@ -13298,210 +13871,218 @@ var file_rpc_proto_depIdxs = []int32{ 154, // 83: gctrpc.DataHistoryJobs.results:type_name -> gctrpc.DataHistoryJob 21, // 84: gctrpc.ModifyOrderRequest.pair:type_name -> gctrpc.CurrencyPair 168, // 85: gctrpc.CurrencyStateResponse.currency_states:type_name -> gctrpc.CurrencyState - 9, // 86: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 3, // 87: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer - 9, // 88: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 18, // 89: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 44, // 90: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary - 45, // 91: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins - 46, // 92: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins - 180, // 93: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - 182, // 94: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - 80, // 95: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses - 18, // 96: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 0, // 97: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest - 6, // 98: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest - 5, // 99: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 5, // 100: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 8, // 101: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest - 2, // 102: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest - 12, // 103: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest - 11, // 104: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 105: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 106: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest - 15, // 107: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest - 11, // 108: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 20, // 109: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest - 23, // 110: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest - 26, // 111: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest - 29, // 112: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest - 32, // 113: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 114: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 115: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest - 36, // 116: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest - 39, // 117: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest - 41, // 118: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest - 48, // 119: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest - 49, // 120: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest - 50, // 121: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest - 53, // 122: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest - 58, // 123: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest - 60, // 124: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest - 61, // 125: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest - 64, // 126: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest - 66, // 127: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest - 67, // 128: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest - 68, // 129: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest - 70, // 130: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest - 72, // 131: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest - 75, // 132: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest - 77, // 133: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest - 78, // 134: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest - 82, // 135: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest - 84, // 136: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest - 86, // 137: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest - 87, // 138: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest - 89, // 139: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest - 91, // 140: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest - 92, // 141: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest - 99, // 142: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest - 101, // 143: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest - 102, // 144: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest - 104, // 145: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest - 105, // 146: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest - 106, // 147: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest - 107, // 148: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest - 108, // 149: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest - 109, // 150: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest - 120, // 151: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest - 125, // 152: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest - 126, // 153: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest - 123, // 154: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest - 127, // 155: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest - 121, // 156: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest - 122, // 157: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest - 124, // 158: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest - 128, // 159: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest - 115, // 160: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest - 132, // 161: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest - 133, // 162: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest - 134, // 163: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest - 135, // 164: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest - 137, // 165: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest - 139, // 166: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest - 140, // 167: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest - 143, // 168: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest - 144, // 169: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest - 111, // 170: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 171: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 172: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest - 114, // 173: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest - 145, // 174: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest - 146, // 175: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest - 148, // 176: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest - 149, // 177: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest - 153, // 178: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 0, // 179: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest - 157, // 180: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest - 153, // 181: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 158, // 182: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest - 159, // 183: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest - 58, // 184: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest - 160, // 185: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest - 162, // 186: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest - 163, // 187: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest - 166, // 188: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest - 165, // 189: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest - 164, // 190: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest - 1, // 191: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse - 7, // 192: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse - 131, // 193: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse - 131, // 194: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse - 10, // 195: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse - 4, // 196: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse - 13, // 197: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse - 131, // 198: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse - 19, // 199: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse - 14, // 200: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse - 16, // 201: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse - 131, // 202: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse - 22, // 203: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse - 25, // 204: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse - 28, // 205: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse - 31, // 206: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse - 35, // 207: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 208: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 209: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse - 37, // 210: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse - 40, // 211: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse - 47, // 212: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse - 131, // 213: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse - 131, // 214: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse - 52, // 215: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse - 55, // 216: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse - 59, // 217: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse - 56, // 218: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails - 63, // 219: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse - 65, // 220: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse - 65, // 221: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse - 131, // 222: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse - 69, // 223: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse - 71, // 224: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse - 74, // 225: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse - 76, // 226: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse - 131, // 227: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse - 81, // 228: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse - 83, // 229: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse - 85, // 230: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse - 88, // 231: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse - 88, // 232: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse - 90, // 233: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse - 93, // 234: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 93, // 235: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 100, // 236: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 100, // 237: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 103, // 238: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse - 131, // 239: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse - 28, // 240: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse - 28, // 241: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse - 22, // 242: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse - 22, // 243: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse - 110, // 244: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse - 131, // 245: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse - 131, // 246: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse - 130, // 247: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse - 129, // 248: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse - 130, // 249: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse - 131, // 250: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse - 131, // 251: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse - 129, // 252: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse - 131, // 253: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse - 116, // 254: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 131, // 255: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse - 131, // 256: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse - 131, // 257: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse - 136, // 258: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse - 138, // 259: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse - 131, // 260: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse - 142, // 261: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse - 131, // 262: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse - 131, // 263: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse - 113, // 264: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 265: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 266: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse - 116, // 267: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 147, // 268: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 147, // 269: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 131, // 270: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse - 152, // 271: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse - 154, // 272: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob - 156, // 273: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs - 156, // 274: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs - 154, // 275: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob - 131, // 276: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse - 131, // 277: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse - 59, // 278: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse - 161, // 279: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse - 167, // 280: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse - 131, // 281: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse - 131, // 282: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse - 131, // 283: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse - 131, // 284: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse - 191, // [191:285] is the sub-list for method output_type - 97, // [97:191] is the sub-list for method input_type - 97, // [97:97] is the sub-list for extension type_name - 97, // [97:97] is the sub-list for extension extendee - 0, // [0:97] is the sub-list for field type_name + 21, // 86: gctrpc.GetFuturesPositionsRequest.pair:type_name -> gctrpc.CurrencyPair + 171, // 87: gctrpc.GetFuturesPositionsResponse.positions:type_name -> gctrpc.FuturePosition + 56, // 88: gctrpc.FuturePosition.orders:type_name -> gctrpc.OrderDetails + 174, // 89: gctrpc.GetCollateralResponse.currencyBreakdown:type_name -> gctrpc.CollateralForCurrency + 9, // 90: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 3, // 91: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer + 9, // 92: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 18, // 93: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 44, // 94: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary + 45, // 95: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins + 46, // 96: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins + 186, // 97: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + 188, // 98: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + 80, // 99: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses + 18, // 100: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 0, // 101: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest + 6, // 102: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest + 5, // 103: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 5, // 104: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 8, // 105: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest + 2, // 106: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest + 12, // 107: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest + 11, // 108: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 109: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 110: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest + 15, // 111: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest + 11, // 112: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 20, // 113: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest + 23, // 114: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest + 26, // 115: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest + 29, // 116: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest + 32, // 117: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 118: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 119: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest + 36, // 120: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest + 39, // 121: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest + 41, // 122: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest + 48, // 123: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest + 49, // 124: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest + 50, // 125: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest + 53, // 126: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest + 58, // 127: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest + 60, // 128: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest + 61, // 129: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest + 64, // 130: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest + 66, // 131: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest + 67, // 132: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest + 68, // 133: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest + 70, // 134: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest + 72, // 135: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest + 75, // 136: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest + 77, // 137: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest + 78, // 138: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest + 82, // 139: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest + 84, // 140: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest + 86, // 141: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest + 87, // 142: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest + 89, // 143: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest + 91, // 144: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest + 92, // 145: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest + 99, // 146: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest + 101, // 147: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest + 102, // 148: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest + 104, // 149: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest + 105, // 150: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest + 106, // 151: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest + 107, // 152: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest + 108, // 153: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest + 109, // 154: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest + 120, // 155: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest + 125, // 156: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest + 126, // 157: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest + 123, // 158: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest + 127, // 159: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest + 121, // 160: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest + 122, // 161: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest + 124, // 162: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest + 128, // 163: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest + 115, // 164: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest + 132, // 165: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest + 133, // 166: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest + 134, // 167: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest + 135, // 168: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest + 137, // 169: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest + 139, // 170: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest + 140, // 171: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest + 143, // 172: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest + 144, // 173: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest + 111, // 174: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 175: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 176: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest + 114, // 177: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest + 145, // 178: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest + 146, // 179: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest + 148, // 180: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest + 149, // 181: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest + 153, // 182: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 0, // 183: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest + 157, // 184: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest + 153, // 185: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 158, // 186: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest + 159, // 187: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest + 58, // 188: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest + 160, // 189: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest + 162, // 190: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest + 163, // 191: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest + 166, // 192: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest + 165, // 193: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest + 164, // 194: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest + 169, // 195: gctrpc.GoCryptoTrader.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest + 172, // 196: gctrpc.GoCryptoTrader.GetCollateral:input_type -> gctrpc.GetCollateralRequest + 1, // 197: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse + 7, // 198: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse + 131, // 199: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse + 131, // 200: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse + 10, // 201: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse + 4, // 202: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse + 13, // 203: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse + 131, // 204: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse + 19, // 205: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse + 14, // 206: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse + 16, // 207: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse + 131, // 208: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse + 22, // 209: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse + 25, // 210: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse + 28, // 211: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse + 31, // 212: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse + 35, // 213: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 214: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 215: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse + 37, // 216: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse + 40, // 217: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse + 47, // 218: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse + 131, // 219: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse + 131, // 220: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse + 52, // 221: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse + 55, // 222: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse + 59, // 223: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse + 56, // 224: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails + 63, // 225: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse + 65, // 226: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse + 65, // 227: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse + 131, // 228: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse + 69, // 229: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse + 71, // 230: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse + 74, // 231: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse + 76, // 232: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse + 131, // 233: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse + 81, // 234: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse + 83, // 235: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse + 85, // 236: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse + 88, // 237: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse + 88, // 238: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse + 90, // 239: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse + 93, // 240: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 93, // 241: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 100, // 242: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 100, // 243: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 103, // 244: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse + 131, // 245: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse + 28, // 246: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse + 28, // 247: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse + 22, // 248: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse + 22, // 249: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse + 110, // 250: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse + 131, // 251: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse + 131, // 252: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse + 130, // 253: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse + 129, // 254: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse + 130, // 255: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse + 131, // 256: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse + 131, // 257: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse + 129, // 258: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse + 131, // 259: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse + 116, // 260: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 131, // 261: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse + 131, // 262: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse + 131, // 263: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse + 136, // 264: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse + 138, // 265: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse + 131, // 266: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse + 142, // 267: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse + 131, // 268: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse + 131, // 269: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse + 113, // 270: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 271: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 272: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse + 116, // 273: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 147, // 274: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 147, // 275: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 131, // 276: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse + 152, // 277: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse + 154, // 278: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob + 156, // 279: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs + 156, // 280: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs + 154, // 281: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob + 131, // 282: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse + 131, // 283: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse + 59, // 284: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse + 161, // 285: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse + 167, // 286: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse + 131, // 287: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse + 131, // 288: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse + 131, // 289: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse + 131, // 290: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse + 170, // 291: gctrpc.GoCryptoTrader.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse + 173, // 292: gctrpc.GoCryptoTrader.GetCollateral:output_type -> gctrpc.GetCollateralResponse + 197, // [197:293] is the sub-list for method output_type + 101, // [101:197] is the sub-list for method input_type + 101, // [101:101] is the sub-list for extension type_name + 101, // [101:101] is the sub-list for extension extendee + 0, // [0:101] is the sub-list for field type_name } func init() { file_rpc_proto_init() } @@ -15538,7 +16119,79 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[179].Exporter = func(v interface{}, i int) interface{} { + file_rpc_proto_msgTypes[169].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFuturesPositionsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[170].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFuturesPositionsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[171].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FuturePosition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[172].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCollateralRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[173].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCollateralResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[174].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CollateralForCurrency); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[185].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelBatchOrdersResponse_Orders); i { case 0: return &v.state @@ -15550,7 +16203,7 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[181].Exporter = func(v interface{}, i int) interface{} { + file_rpc_proto_msgTypes[187].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelAllOrdersResponse_Orders); i { case 0: return &v.state @@ -15569,7 +16222,7 @@ func file_rpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rpc_proto_rawDesc, NumEnums: 0, - NumMessages: 185, + NumMessages: 191, NumExtensions: 0, NumServices: 1, }, diff --git a/gctrpc/rpc.pb.gw.go b/gctrpc/rpc.pb.gw.go index 8a046caac53..763502513d5 100644 --- a/gctrpc/rpc.pb.gw.go +++ b/gctrpc/rpc.pb.gw.go @@ -3023,6 +3023,78 @@ func local_request_GoCryptoTrader_CurrencyStateTradingPair_0(ctx context.Context } +var ( + filter_GoCryptoTrader_GetFuturesPositions_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_GetFuturesPositions_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetFuturesPositionsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetFuturesPositions_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetFuturesPositions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_GetFuturesPositions_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetFuturesPositionsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetFuturesPositions_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetFuturesPositions(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_GetCollateral_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_GetCollateral_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCollateralRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetCollateral_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetCollateral(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_GetCollateral_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCollateralRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetCollateral_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetCollateral(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterGoCryptoTraderHandlerServer registers the http handlers for service GoCryptoTrader to "mux". // UnaryRPC :call GoCryptoTraderServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -5095,6 +5167,52 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetFuturesPositions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetFuturesPositions", runtime.WithHTTPPathPattern("/v1/getfuturespositions")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_GetFuturesPositions_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetFuturesPositions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_GetCollateral_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetCollateral", runtime.WithHTTPPathPattern("/v1/getcollateral")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_GetCollateral_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetCollateral_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7016,6 +7134,46 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetFuturesPositions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetFuturesPositions", runtime.WithHTTPPathPattern("/v1/getfuturespositions")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_GetFuturesPositions_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetFuturesPositions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_GetCollateral_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetCollateral", runtime.WithHTTPPathPattern("/v1/getcollateral")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_GetCollateral_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetCollateral_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7207,6 +7365,10 @@ var ( pattern_GoCryptoTrader_CurrencyStateWithdraw_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "currencystatewithdraw"}, "")) pattern_GoCryptoTrader_CurrencyStateTradingPair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "currencystatetradingpair"}, "")) + + pattern_GoCryptoTrader_GetFuturesPositions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getfuturespositions"}, "")) + + pattern_GoCryptoTrader_GetCollateral_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getcollateral"}, "")) ) var ( @@ -7397,4 +7559,8 @@ var ( forward_GoCryptoTrader_CurrencyStateWithdraw_0 = runtime.ForwardResponseMessage forward_GoCryptoTrader_CurrencyStateTradingPair_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_GetFuturesPositions_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_GetCollateral_0 = runtime.ForwardResponseMessage ) diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 950b34ba197..601cedad4bf 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1023,6 +1023,55 @@ message CurrencyState { bool trading_enabled = 5; } +message GetFuturesPositionsRequest { + string exchange =1; + string asset = 2; + CurrencyPair pair = 3; + string start_date = 4; + string end_date = 5; + string status = 6; + int64 positionLimit = 7; + bool verbose = 8; +} + +message GetFuturesPositionsResponse { + int64 totalOrders = 1; + double totalRealisedPNL = 2; + double totalUnrealisedPNL = 3; + double totalPNL = 4; + repeated FuturePosition positions = 5; +} + +message FuturePosition { + string status = 1; + string currentDirection = 2; + string unrealisedPNL = 3; + string realisedPNL = 4; + string openingDate = 5; + string closingDate = 6; + repeated OrderDetails orders = 7; +} + +message GetCollateralRequest { + string exchange =1; + string asset = 2; + string subAccount = 3; + bool includeBreakdown = 4; + bool calculateOffline = 5; +} + +message GetCollateralResponse { + string totalCollateral = 1; + repeated CollateralForCurrency currencyBreakdown = 2; +} + +message CollateralForCurrency { + string currency = 1; + string scaledCollateral = 2; + string scaledToCurrency = 3; +} + + service GoCryptoTrader { rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) { option (google.api.http) = { @@ -1612,4 +1661,14 @@ service GoCryptoTrader { get: "/v1/currencystatetradingpair" }; } + rpc GetFuturesPositions (GetFuturesPositionsRequest) returns (GetFuturesPositionsResponse) { + option (google.api.http) = { + get: "/v1/getfuturespositions" + }; + } + rpc GetCollateral (GetCollateralRequest) returns (GetCollateralResponse) { + option (google.api.http) = { + get: "/v1/getcollateral" + }; + } } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index e35e6a77a3c..a336d1b6f03 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1159,6 +1159,60 @@ ] } }, + "/v1/getcollateral": { + "get": { + "operationId": "GoCryptoTrader_GetCollateral", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetCollateralResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "asset", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "subAccount", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "includeBreakdown", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "calculateOffline", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/getcommunicationrelayers": { "get": { "operationId": "GoCryptoTrader_GetCommunicationRelayers", @@ -1705,6 +1759,91 @@ ] } }, + "/v1/getfuturespositions": { + "get": { + "operationId": "GoCryptoTrader_GetFuturesPositions", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetFuturesPositionsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "asset", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.delimiter", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.base", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.quote", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "startDate", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "endDate", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "positionLimit", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "verbose", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/gethistoriccandles": { "get": { "operationId": "GoCryptoTrader_GetHistoricCandles", @@ -3477,6 +3616,20 @@ } } }, + "gctrpcCollateralForCurrency": { + "type": "object", + "properties": { + "currency": { + "type": "string" + }, + "scaledCollateral": { + "type": "string" + }, + "scaledToCurrency": { + "type": "string" + } + } + }, "gctrpcCommunicationRelayer": { "type": "object", "properties": { @@ -3805,6 +3958,35 @@ } } }, + "gctrpcFuturePosition": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "currentDirection": { + "type": "string" + }, + "unrealisedPNL": { + "type": "string" + }, + "realisedPNL": { + "type": "string" + }, + "openingDate": { + "type": "string" + }, + "closingDate": { + "type": "string" + }, + "orders": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcOrderDetails" + } + } + } + }, "gctrpcGCTScript": { "type": "object", "properties": { @@ -3970,6 +4152,20 @@ } } }, + "gctrpcGetCollateralResponse": { + "type": "object", + "properties": { + "totalCollateral": { + "type": "string" + }, + "currencyBreakdown": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcCollateralForCurrency" + } + } + } + }, "gctrpcGetCommunicationRelayersResponse": { "type": "object", "properties": { @@ -4181,6 +4377,33 @@ } } }, + "gctrpcGetFuturesPositionsResponse": { + "type": "object", + "properties": { + "totalOrders": { + "type": "string", + "format": "int64" + }, + "totalRealisedPNL": { + "type": "number", + "format": "double" + }, + "totalUnrealisedPNL": { + "type": "number", + "format": "double" + }, + "totalPNL": { + "type": "number", + "format": "double" + }, + "positions": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcFuturePosition" + } + } + } + }, "gctrpcGetHistoricCandlesResponse": { "type": "object", "properties": { diff --git a/gctrpc/rpc_grpc.pb.go b/gctrpc/rpc_grpc.pb.go index 3f0e90f383c..3bd6cc9061e 100644 --- a/gctrpc/rpc_grpc.pb.go +++ b/gctrpc/rpc_grpc.pb.go @@ -112,6 +112,8 @@ type GoCryptoTraderClient interface { CurrencyStateDeposit(ctx context.Context, in *CurrencyStateDepositRequest, opts ...grpc.CallOption) (*GenericResponse, error) CurrencyStateWithdraw(ctx context.Context, in *CurrencyStateWithdrawRequest, opts ...grpc.CallOption) (*GenericResponse, error) CurrencyStateTradingPair(ctx context.Context, in *CurrencyStateTradingPairRequest, opts ...grpc.CallOption) (*GenericResponse, error) + GetFuturesPositions(ctx context.Context, in *GetFuturesPositionsRequest, opts ...grpc.CallOption) (*GetFuturesPositionsResponse, error) + GetCollateral(ctx context.Context, in *GetCollateralRequest, opts ...grpc.CallOption) (*GetCollateralResponse, error) } type goCryptoTraderClient struct { @@ -1106,6 +1108,24 @@ func (c *goCryptoTraderClient) CurrencyStateTradingPair(ctx context.Context, in return out, nil } +func (c *goCryptoTraderClient) GetFuturesPositions(ctx context.Context, in *GetFuturesPositionsRequest, opts ...grpc.CallOption) (*GetFuturesPositionsResponse, error) { + out := new(GetFuturesPositionsResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GetFuturesPositions", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) GetCollateral(ctx context.Context, in *GetCollateralRequest, opts ...grpc.CallOption) (*GetCollateralResponse, error) { + out := new(GetCollateralResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GetCollateral", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GoCryptoTraderServer is the server API for GoCryptoTrader service. // All implementations must embed UnimplementedGoCryptoTraderServer // for forward compatibility @@ -1204,6 +1224,8 @@ type GoCryptoTraderServer interface { CurrencyStateDeposit(context.Context, *CurrencyStateDepositRequest) (*GenericResponse, error) CurrencyStateWithdraw(context.Context, *CurrencyStateWithdrawRequest) (*GenericResponse, error) CurrencyStateTradingPair(context.Context, *CurrencyStateTradingPairRequest) (*GenericResponse, error) + GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) + GetCollateral(context.Context, *GetCollateralRequest) (*GetCollateralResponse, error) mustEmbedUnimplementedGoCryptoTraderServer() } @@ -1493,6 +1515,12 @@ func (UnimplementedGoCryptoTraderServer) CurrencyStateWithdraw(context.Context, func (UnimplementedGoCryptoTraderServer) CurrencyStateTradingPair(context.Context, *CurrencyStateTradingPairRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CurrencyStateTradingPair not implemented") } +func (UnimplementedGoCryptoTraderServer) GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetFuturesPositions not implemented") +} +func (UnimplementedGoCryptoTraderServer) GetCollateral(context.Context, *GetCollateralRequest) (*GetCollateralResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCollateral not implemented") +} func (UnimplementedGoCryptoTraderServer) mustEmbedUnimplementedGoCryptoTraderServer() {} // UnsafeGoCryptoTraderServer may be embedded to opt out of forward compatibility for this service. @@ -3216,6 +3244,42 @@ func _GoCryptoTrader_CurrencyStateTradingPair_Handler(srv interface{}, ctx conte return interceptor(ctx, in, info, handler) } +func _GoCryptoTrader_GetFuturesPositions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetFuturesPositionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).GetFuturesPositions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/GetFuturesPositions", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).GetFuturesPositions(ctx, req.(*GetFuturesPositionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_GetCollateral_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCollateralRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).GetCollateral(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/GetCollateral", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).GetCollateral(ctx, req.(*GetCollateralRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GoCryptoTrader_ServiceDesc is the grpc.ServiceDesc for GoCryptoTrader service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -3575,6 +3639,14 @@ var GoCryptoTrader_ServiceDesc = grpc.ServiceDesc{ MethodName: "CurrencyStateTradingPair", Handler: _GoCryptoTrader_CurrencyStateTradingPair_Handler, }, + { + MethodName: "GetFuturesPositions", + Handler: _GoCryptoTrader_GetFuturesPositions_Handler, + }, + { + MethodName: "GetCollateral", + Handler: _GoCryptoTrader_GetCollateral_Handler, + }, }, Streams: []grpc.StreamDesc{ { From 44d17802549ac1352e9905af583602c639505bea Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 7 Jan 2022 11:03:54 +1100 Subject: [PATCH 002/171] lint and test fixes --- .../eventhandlers/exchange/exchange_test.go | 4 +- engine/exchange_manager.go | 4 +- engine/order_manager.go | 2 +- engine/order_manager_test.go | 4 +- engine/rpcserver.go | 1 - engine/rpcserver_test.go | 14 ++-- exchanges/btcmarkets/btcmarkets.go | 6 +- exchanges/ftx/ftx.go | 8 --- exchanges/ftx/ftx_test.go | 3 + exchanges/ftx/ftx_websocket.go | 1 + exchanges/ftx/ftx_websocket_test.go | 2 +- exchanges/ftx/ftx_wrapper.go | 42 +++++------- exchanges/order/futures.go | 17 +++-- exchanges/order/futures_test.go | 67 ++++++++++--------- exchanges/order/futures_types.go | 2 - gctscript/vm/manager.go | 1 + log/logger_multiwriter.go | 12 ++-- log/logger_setup.go | 2 +- log/logger_test.go | 12 ++-- log/logger_types.go | 2 +- 20 files changed, 99 insertions(+), 107 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 5c1bc6f0f94..7ca93788905 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -171,8 +171,8 @@ func TestPlaceOrder(t *testing.T) { } f := &fill.Fill{} _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, f, bot.OrderManager) - if err != nil && err.Error() != "order exchange name must be specified" { - t.Error(err) + if !errors.Is(err, engine.ErrExchangeNameIsEmpty) { + t.Errorf("received: %v, expected: %v", err, engine.ErrExchangeNameIsEmpty) } f.Exchange = testExchange diff --git a/engine/exchange_manager.go b/engine/exchange_manager.go index a9dab9ba935..1d0ac43b35d 100644 --- a/engine/exchange_manager.go +++ b/engine/exchange_manager.go @@ -42,7 +42,7 @@ var ( ErrExchangeNotFound = errors.New("exchange not found") ErrExchangeAlreadyLoaded = errors.New("exchange already loaded") ErrExchangeFailedToLoad = errors.New("exchange failed to load") - errExchangeNameIsEmpty = errors.New("exchange name is empty") + ErrExchangeNameIsEmpty = errors.New("exchange name is empty") ) // CustomExchangeBuilder interface allows external applications to create @@ -111,7 +111,7 @@ func (m *ExchangeManager) GetExchangeByName(exchangeName string) (exchange.IBotE return nil, fmt.Errorf("exchange manager: %w", ErrNilSubsystem) } if exchangeName == "" { - return nil, fmt.Errorf("exchange manager: %w", errExchangeNameIsEmpty) + return nil, fmt.Errorf("exchange manager: %w", ErrExchangeNameIsEmpty) } m.m.Lock() defer m.m.Unlock() diff --git a/engine/order_manager.go b/engine/order_manager.go index 9a400d0eedc..e4731cd525a 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -284,7 +284,7 @@ func (m *OrderManager) validate(newOrder *order.Submit) error { } if newOrder.Exchange == "" { - return errExchangeNameIsEmpty + return ErrExchangeNameIsEmpty } if err := newOrder.Validate(); err != nil { diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 9816ce00031..668253db351 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -1200,8 +1200,8 @@ func TestSubmitFakeOrder(t *testing.T) { } ord := &order.Submit{} _, err = o.SubmitFakeOrder(ord, resp, false) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", err, errExchangeNameIsEmpty) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", err, ErrExchangeNameIsEmpty) } ord.Exchange = testExchange ord.AssetType = asset.Spot diff --git a/engine/rpcserver.go b/engine/rpcserver.go index f71747f2090..394ec52d795 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4253,7 +4253,6 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if strings.EqualFold(r.SubAccount, ai.Accounts[i].ID) { acc = ai.Accounts[i] break - } } } else if len(ai.Accounts) > 0 { diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 892d65cdaac..858ff86af1f 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -113,7 +113,7 @@ func (f fExchange) FetchAccountInfo(_ context.Context, a asset.Item) (account.Ho } // GetFuturesPositions overrides testExchange's GetFuturesPositions function -func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp currency.Pair, _ time.Time, _ time.Time) ([]order.Detail, error) { +func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp currency.Pair, _, _ time.Time) ([]order.Detail, error) { return []order.Detail{ { Price: 1337, @@ -537,8 +537,8 @@ func TestGetHistoricCandles(t *testing.T) { End: defaultEnd.Format(common.SimpleTimeFormat), AssetType: asset.Spot.String(), }) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", err, errExchangeNameIsEmpty) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", err, ErrExchangeNameIsEmpty) } _, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{ @@ -1113,8 +1113,8 @@ func TestGetOrders(t *testing.T) { AssetType: asset.Spot.String(), Pair: p, }) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", errExchangeNameIsEmpty, err) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", ErrExchangeNameIsEmpty, err) } _, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{ @@ -1750,8 +1750,8 @@ func TestGetManagedOrders(t *testing.T) { AssetType: asset.Spot.String(), Pair: p, }) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", errExchangeNameIsEmpty, err) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", ErrExchangeNameIsEmpty, err) } _, err = s.GetManagedOrders(context.Background(), &gctrpc.GetOrdersRequest{ diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index b707fdcdf90..7860ac6b2ae 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -28,10 +28,10 @@ const ( btcMarketsAllMarkets = "/markets/" btcMarketsGetTicker = "/ticker/" btcMarketsGetTrades = "/trades?" - btcMarketOrderBooks = "/orderbook?" + btcMarketOrderBook = "/orderbook?" btcMarketsCandles = "/candles?" btcMarketsTickers = "tickers?" - btcMarketsMultipleOrderbooks = "/orderbooks?" + btcMarketsMultipleOrderbooks = "orderbooks?" btcMarketsGetTime = "/time" btcMarketsWithdrawalFees = "/withdrawal-fees" btcMarketsUnauthPath = btcMarketsAPIURL + btcMarketsAPIVersion + btcMarketsAllMarkets @@ -120,7 +120,7 @@ func (b *BTCMarkets) GetOrderbook(ctx context.Context, marketID string, level in if level != 0 { params.Set("level", strconv.FormatInt(level, 10)) } - err := b.SendHTTPRequest(ctx, btcMarketsUnauthPath+marketID+btcMarketOrderBooks+params.Encode(), + err := b.SendHTTPRequest(ctx, btcMarketsUnauthPath+marketID+btcMarketOrderBook+params.Encode(), &temp) if err != nil { return orderbook, err diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index a351c6873b3..aa34fc525a3 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1476,14 +1476,6 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err return limits, nil } -func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64, side order.Side) (float64, error) { - collateralWeight, ok := f.collateralWeight[code.Upper().String()] - if !ok { - return 0, errCoinMustBeSpecified - } - return collateralWeight.Total * positionSize, nil -} - // LoadCollateralWeightings sets the collateral weights for // currencies supported by FTX func (f *FTX) LoadCollateralWeightings() error { diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 9f4ac5738bd..8c9d0ed110e 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1864,6 +1864,9 @@ func TestCalculatePNL(t *testing.T) { f.Verbose = true pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) positions, err := f.GetFuturesPositions(context.Background(), asset.Futures, pair, time.Date(2021, 1, 6, 4, 28, 0, 0, time.UTC), time.Date(2021, 12, 31, 4, 32, 0, 0, time.UTC)) + if err != nil { + t.Error(err) + } var orders []order.Detail for i := range positions { orders = append(orders, order.Detail{ diff --git a/exchanges/ftx/ftx_websocket.go b/exchanges/ftx/ftx_websocket.go index 311b39bd8e8..2c2c6fa42d5 100644 --- a/exchanges/ftx/ftx_websocket.go +++ b/exchanges/ftx/ftx_websocket.go @@ -563,6 +563,7 @@ func (f *FTX) WsProcessPartialOB(data *WsOrderbookData, p currency.Pair, a asset Exchange: f.Name, VerifyOrderbook: f.CanVerifyOrderbook, } + return f.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index f6787fb49d6..5492ca19006 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -359,7 +359,7 @@ func TestParsingMarketsData(t *testing.T) { "future": { "name": "ADA-0626", "underlying": "ADA", - "description": "Cardano June 2020 FuturesTracker", + "description": "Cardano June 2020 Futures", "type": "future", "expiry": "2020-06-26T003:00:00+00:00", "perpetual": false, "expired": false, diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 213cacfef11..8eace2ef2b1 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -186,22 +186,20 @@ func (f *FTX) Setup(exch *config.Exchange) error { return err } - if exch.Websocket != nil && *exch.Websocket { - err = f.Websocket.Setup(&stream.WebsocketSetup{ - ExchangeConfig: exch, - DefaultURL: ftxWSURL, - RunningURL: wsEndpoint, - Connector: f.WsConnect, - Subscriber: f.Subscribe, - Unsubscriber: f.Unsubscribe, - GenerateSubscriptions: f.GenerateDefaultSubscriptions, - Features: &f.Features.Supports.WebsocketCapabilities, - TradeFeed: f.Features.Enabled.TradeFeed, - FillsFeed: f.Features.Enabled.FillsFeed, - }) - if err != nil { - return err - } + err = f.Websocket.Setup(&stream.WebsocketSetup{ + ExchangeConfig: exch, + DefaultURL: ftxWSURL, + RunningURL: wsEndpoint, + Connector: f.WsConnect, + Subscriber: f.Subscribe, + Unsubscriber: f.Unsubscribe, + GenerateSubscriptions: f.GenerateDefaultSubscriptions, + Features: &f.Features.Supports.WebsocketCapabilities, + TradeFeed: f.Features.Enabled.TradeFeed, + FillsFeed: f.Features.Enabled.FillsFeed, + }) + if err != nil { + return err } if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { @@ -213,13 +211,10 @@ func (f *FTX) Setup(exch *config.Exchange) error { err) } } - if exch.Websocket != nil && *exch.Websocket { - return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - }) - } - return nil + return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + }) } // Start starts the FTX go routine @@ -264,7 +259,6 @@ func (f *FTX) Run() { f.Name, err) } - } // FetchTradablePairs returns a list of the exchanges tradable pairs diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 7a9488c4948..b8f87b4f97d 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -313,7 +313,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if !p.contractPair.Equal(d.Pair) { return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.contractPair) } - if p.exchange != strings.ToLower(d.Exchange) { + if !strings.EqualFold(p.exchange, d.Exchange) { return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.exchange) } if p.asset != d.AssetType { @@ -354,18 +354,15 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } else { p.longPositions = append(p.longPositions, d.Copy()) } - var shortSide, longSide, averageLeverage decimal.Decimal + var shortSide, longSide decimal.Decimal for i := range p.shortPositions { shortSide = shortSide.Add(decimal.NewFromFloat(p.shortPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(p.shortPositions[i].Leverage) } for i := range p.longPositions { longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(p.longPositions[i].Leverage) } - averageLeverage.Div(decimal.NewFromInt(int64(len(p.shortPositions))).Add(decimal.NewFromInt(int64(len(p.longPositions))))) if p.currentDirection == "" { p.currentDirection = d.Side } @@ -428,7 +425,6 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { cal.Time = cal.Time.Add(1) cal.PNLHistory = p.pnlHistory result, err = p.PNLCalculation.CalculatePNL(cal) - } else { result, err = p.PNLCalculation.CalculatePNL(cal) } @@ -446,18 +442,21 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } p.unrealisedPNL = result.UnrealisedPNL - if longSide.GreaterThan(shortSide) { + switch { + case longSide.GreaterThan(shortSide): p.currentDirection = Long - } else if shortSide.GreaterThan(longSide) { + case shortSide.GreaterThan(longSide): p.currentDirection = Short - } else { + default: p.currentDirection = UnknownSide } + if p.currentDirection.IsLong() { p.exposure = longSide.Sub(shortSide) } else { p.exposure = shortSide.Sub(longSide) } + if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 4bc0d815ac4..3ed079969e8 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -11,11 +11,15 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) +const testExchange = "test" + +// FakePNL is implements PNL interface type FakePNL struct { err error result *PNLResult } +// CalculatePNL overrides default pnl calculations func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { if f.err != nil { return nil, f.err @@ -55,14 +59,14 @@ func TestUpsertPNLEntry(t *testing.T) { func TestTrackNewOrder(t *testing.T) { t.Parallel() - exch := "test" + exch := testExchange item := asset.Futures pair, err := currency.NewPairFromStrings("BTC", "1231") if !errors.Is(err, nil) { t.Error(err) } e := MultiPositionTracker{ - exchange: "test", + exchange: testExchange, exchangePNLCalculation: &FakePNL{}, } setup := &PositionTrackerSetup{ @@ -190,7 +194,7 @@ func TestSetupMultiPositionTracker(t *testing.T) { if !errors.Is(err, errExchangeNameEmpty) { t.Error(err) } - setup.Exchange = "test" + setup.Exchange = testExchange _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, ErrNotFutureAsset) { t.Error(err) @@ -224,14 +228,14 @@ func TestSetupMultiPositionTracker(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if resp.exchange != "test" { + if resp.exchange != testExchange { t.Errorf("expected 'test' received %v", resp.exchange) } } func TestExchangeTrackNewOrder(t *testing.T) { t.Parallel() - exch := "test" + exch := testExchange item := asset.Futures pair := currency.NewPair(currency.BTC, currency.USDT) setup := &MultiPositionTrackerSetup{ @@ -309,6 +313,9 @@ func TestExchangeTrackNewOrder(t *testing.T) { ID: "2", Amount: 2, }) + if !errors.Is(err, nil) { + t.Error(err) + } if len(resp.positions) != 2 { t.Errorf("expected '2' received %v", len(resp.positions)) } @@ -411,7 +418,7 @@ func TestGetStats(t *testing.T) { t.Error("expected 0") } - p.exchange = "test" + p.exchange = testExchange stats = p.GetStats() if stats.Exchange != p.exchange { t.Errorf("expected '%v' received '%v'", p.exchange, stats.Exchange) @@ -433,13 +440,13 @@ func TestGetPositions(t *testing.T) { } p.positions = append(p.positions, &PositionTracker{ - exchange: "test", + exchange: testExchange, }) positions = p.GetPositions() if len(positions) != 1 { t.Fatal("expected 1") } - if positions[0].exchange != "test" { + if positions[0].exchange != testExchange { t.Error("expected 'test'") } @@ -448,14 +455,13 @@ func TestGetPositions(t *testing.T) { if len(positions) > 0 { t.Error("expected 0") } - } func TestGetPositionsForExchange(t *testing.T) { t.Parallel() c := &PositionController{} p := currency.NewPair(currency.BTC, currency.USDT) - pos, err := c.GetPositionsForExchange("test", asset.Futures, p) + pos, err := c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } @@ -463,54 +469,54 @@ func TestGetPositionsForExchange(t *testing.T) { t.Error("expected zero") } c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers["test"] = nil - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + c.positionTrackerControllers[testExchange] = nil + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForAsset) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - c.positionTrackerControllers["test"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers["test"][asset.Futures] = nil - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + c.positionTrackerControllers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures] = nil + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForPair) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) } - pos, err = c.GetPositionsForExchange("test", asset.Spot, p) + _, err = c.GetPositionsForExchange(testExchange, asset.Spot, p) if !errors.Is(err, ErrNotFutureAsset) { t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) } - c.positionTrackerControllers["test"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ - exchange: "test", + c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, } - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + pos, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v", err, nil) } if len(pos) != 0 { t.Fatal("expected zero") } - c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ - exchange: "test", + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, positions: []*PositionTracker{ { - exchange: "test", + exchange: testExchange, }, }, } - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + pos, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v", err, nil) } if len(pos) != 1 { t.Fatal("expected 1") } - if pos[0].exchange != "test" { + if pos[0].exchange != testExchange { t.Error("expected test") } c = nil - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, common.ErrNilPointer) { t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) } @@ -556,7 +562,7 @@ func TestSetupPositionTracker(t *testing.T) { if p != nil { t.Error("expected nil") } - m.exchange = "test" + m.exchange = testExchange p, err = m.SetupPositionTracker(nil) if !errors.Is(err, errNilSetup) { t.Errorf("received '%v' expected '%v", err, errNilSetup) @@ -594,13 +600,13 @@ func TestSetupPositionTracker(t *testing.T) { t.Errorf("received '%v' expected '%v", err, nil) } if p == nil { - t.Error("expected nil") + t.Fatal("expected not nil") } - if p.exchange != "test" { + if p.exchange != testExchange { t.Error("expected test") } - p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + _, err = m.SetupPositionTracker(&PositionTrackerSetup{ Asset: asset.Futures, Pair: cp, UseExchangePNLCalculation: true, @@ -641,7 +647,6 @@ func TestCalculatePNL(t *testing.T) { if !errors.Is(err, errCannotCalculateUnrealisedPNL) { t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) } - } func TestTrackPNLByTime(t *testing.T) { diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 4052dca6576..185c8f5895a 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -96,7 +96,6 @@ type MultiPositionTracker struct { // order positions allows for an easier time knowing which order is // part of which position tracker orderPositions map[string]*PositionTracker - pnl decimal.Decimal offlinePNLCalculation bool useExchangePNLCalculations bool exchangePNLCalculation PNLCalculation @@ -131,7 +130,6 @@ type PositionTracker struct { currentDirection Side openingDirection Side status Status - averageLeverage decimal.Decimal unrealisedPNL decimal.Decimal realisedPNL decimal.Decimal shortPositions []Detail diff --git a/gctscript/vm/manager.go b/gctscript/vm/manager.go index 294b5d25dad..f29fec27491 100644 --- a/gctscript/vm/manager.go +++ b/gctscript/vm/manager.go @@ -16,6 +16,7 @@ const ( Name = "gctscript" ) +// ErrNilSubsystem returned when script manager has not been set up var ErrNilSubsystem = errors.New("gct script has not been set up") // GctScriptManager loads and runs GCT Tengo scripts diff --git a/log/logger_multiwriter.go b/log/logger_multiwriter.go index ba9550dcd73..4c7639706ea 100644 --- a/log/logger_multiwriter.go +++ b/log/logger_multiwriter.go @@ -12,7 +12,7 @@ var ( ) // Add appends a new writer to the multiwriter slice -func (mw *multiWriter) Add(writer io.Writer) error { +func (mw *multiWriterHolder) Add(writer io.Writer) error { mw.mu.Lock() defer mw.mu.Unlock() for i := range mw.writers { @@ -25,7 +25,7 @@ func (mw *multiWriter) Add(writer io.Writer) error { } // Remove removes existing writer from multiwriter slice -func (mw *multiWriter) Remove(writer io.Writer) error { +func (mw *multiWriterHolder) Remove(writer io.Writer) error { mw.mu.Lock() defer mw.mu.Unlock() for i := range mw.writers { @@ -41,7 +41,7 @@ func (mw *multiWriter) Remove(writer io.Writer) error { } // Write concurrent safe Write for each writer -func (mw *multiWriter) Write(p []byte) (int, error) { +func (mw *multiWriterHolder) Write(p []byte) (int, error) { type data struct { n int err error @@ -78,9 +78,9 @@ func (mw *multiWriter) Write(p []byte) (int, error) { return len(p), nil } -// MultiWriter make and return a new copy of multiWriter -func MultiWriter(writers ...io.Writer) (*multiWriter, error) { - mw := &multiWriter{} +// multiWriter make and return a new copy of multiWriterHolder +func multiWriter(writers ...io.Writer) (*multiWriterHolder, error) { + mw := &multiWriterHolder{} for x := range writers { err := mw.Add(writers[x]) if err != nil { diff --git a/log/logger_setup.go b/log/logger_setup.go index 7f815b7fa05..a2785fff199 100644 --- a/log/logger_setup.go +++ b/log/logger_setup.go @@ -39,7 +39,7 @@ func getWriters(s *SubLoggerConfig) (io.Writer, error) { } writers = append(writers, writer) } - return MultiWriter(writers...) + return multiWriter(writers...) } // GenDefaultSettings return struct with known sane/working logger settings diff --git a/log/logger_test.go b/log/logger_test.go index fe14c8b4eb6..6e6d4fcc7ef 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -89,12 +89,12 @@ func BenchmarkInfo(b *testing.B) { func TestAddWriter(t *testing.T) { t.Parallel() - _, err := MultiWriter(ioutil.Discard, ioutil.Discard) + _, err := multiWriter(ioutil.Discard, ioutil.Discard) if !errors.Is(err, errWriterAlreadyLoaded) { t.Fatalf("received: '%v' but expected: '%v'", err, errWriterAlreadyLoaded) } - mw, err := MultiWriter() + mw, err := multiWriter() if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -118,7 +118,7 @@ func TestAddWriter(t *testing.T) { func TestRemoveWriter(t *testing.T) { t.Parallel() - mw, err := MultiWriter() + mw, err := multiWriter() if err != nil { t.Fatal(err) } @@ -169,7 +169,7 @@ var errWriteError = errors.New("write error") func TestMultiWriterWrite(t *testing.T) { t.Parallel() - mw, err := MultiWriter(ioutil.Discard, &bytes.Buffer{}) + mw, err := multiWriter(ioutil.Discard, &bytes.Buffer{}) if err != nil { t.Fatal(err) } @@ -183,7 +183,7 @@ func TestMultiWriterWrite(t *testing.T) { t.Fatal("unexpected return") } - mw, err = MultiWriter(&WriteShorter{}, ioutil.Discard) + mw, err = multiWriter(&WriteShorter{}, ioutil.Discard) if err != nil { t.Fatal(err) } @@ -192,7 +192,7 @@ func TestMultiWriterWrite(t *testing.T) { t.Fatalf("received: '%v' but expected: '%v'", err, io.ErrShortWrite) } - mw, err = MultiWriter(&WriteError{}, ioutil.Discard) + mw, err = multiWriter(&WriteError{}, ioutil.Discard) if err != nil { t.Fatal(err) } diff --git a/log/logger_types.go b/log/logger_types.go index 1ad09846064..f33795d26a6 100644 --- a/log/logger_types.go +++ b/log/logger_types.go @@ -86,7 +86,7 @@ type Levels struct { Info, Debug, Warn, Error bool } -type multiWriter struct { +type multiWriterHolder struct { writers []io.Writer mu sync.RWMutex } From 4f19acc4848a9f89a3f595c545b4f0642f0d5ba7 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 7 Jan 2022 12:58:52 +1100 Subject: [PATCH 003/171] Fix uneven split pnl. Adds collateral weight test. docs. New clear func --- CONTRIBUTORS | 4 +- LICENSE | 2 +- README.md | 12 +- .../engine_templates/order_manager.tmpl | 1 + .../exchanges_templates/orders.tmpl | 2 + cmd/gctcli/commands.go | 16 + engine/order_manager.go | 23 +- engine/order_manager.md | 1 + engine/order_manager_test.go | 29 + engine/order_manager_types.go | 14 +- engine/rpcserver.go | 6 + exchanges/ftx/ftx.go | 16 +- exchanges/ftx/ftx_test.go | 41 +- exchanges/order/futures.go | 53 +- exchanges/order/futures_test.go | 57 + gctrpc/rpc.pb.go | 1524 +++++++++-------- gctrpc/rpc.proto | 1 + gctrpc/rpc.swagger.json | 6 + 18 files changed, 1016 insertions(+), 792 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 2b124eea98f..d7c1b56f45a 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -4,14 +4,14 @@ thrasher- | https://github.com/thrasher- shazbert | https://github.com/shazbert gloriousCode | https://github.com/gloriousCode dependabot-preview[bot] | https://github.com/apps/dependabot-preview -xtda | https://github.com/xtda dependabot[bot] | https://github.com/apps/dependabot +xtda | https://github.com/xtda lrascao | https://github.com/lrascao Rots | https://github.com/Rots vazha | https://github.com/vazha +ydm | https://github.com/ydm ermalguni | https://github.com/ermalguni MadCozBadd | https://github.com/MadCozBadd -ydm | https://github.com/ydm vadimzhukck | https://github.com/vadimzhukck 140am | https://github.com/140am marcofranssen | https://github.com/marcofranssen diff --git a/LICENSE b/LICENSE index a2bfbfcf733..8dfe090d247 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2021 The GoCryptoTrader Developers +Copyright (c) 2014-2022 The GoCryptoTrader Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 3db9a3a8457..8cebce64bf5 100644 --- a/README.md +++ b/README.md @@ -143,18 +143,18 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| -| [thrasher-](https://github.com/thrasher-) | 660 | -| [shazbert](https://github.com/shazbert) | 226 | -| [gloriousCode](https://github.com/gloriousCode) | 191 | +| [thrasher-](https://github.com/thrasher-) | 662 | +| [shazbert](https://github.com/shazbert) | 231 | +| [gloriousCode](https://github.com/gloriousCode) | 194 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | +| [dependabot[bot]](https://github.com/apps/dependabot) | 50 | | [xtda](https://github.com/xtda) | 47 | -| [dependabot[bot]](https://github.com/apps/dependabot) | 29 | -| [lrascao](https://github.com/lrascao) | 15 | +| [lrascao](https://github.com/lrascao) | 21 | | [Rots](https://github.com/Rots) | 15 | | [vazha](https://github.com/vazha) | 15 | +| [ydm](https://github.com/ydm) | 15 | | [ermalguni](https://github.com/ermalguni) | 14 | | [MadCozBadd](https://github.com/MadCozBadd) | 13 | -| [ydm](https://github.com/ydm) | 13 | | [vadimzhukck](https://github.com/vadimzhukck) | 10 | | [140am](https://github.com/140am) | 8 | | [marcofranssen](https://github.com/marcofranssen) | 8 | diff --git a/cmd/documentation/engine_templates/order_manager.tmpl b/cmd/documentation/engine_templates/order_manager.tmpl index a7b0e08e675..1aed6f81dcf 100644 --- a/cmd/documentation/engine_templates/order_manager.tmpl +++ b/cmd/documentation/engine_templates/order_manager.tmpl @@ -4,6 +4,7 @@ + The order manager subsystem stores and monitors all orders from enabled exchanges with API keys and `authenticatedSupport` enabled + It can be enabled or disabled via runtime command `-ordermanager=false` and defaults to true + All orders placed via GoCryptoTrader will be added to the order manager store ++ Any futures based order will be tracked via the [futures positions controller](/exchanges/orders/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ### Please click GoDocs chevron above to view current GoDoc information for this package {{template "contributions"}} diff --git a/cmd/documentation/exchanges_templates/orders.tmpl b/cmd/documentation/exchanges_templates/orders.tmpl index 7538fe18615..78bd5db115b 100644 --- a/cmd/documentation/exchanges_templates/orders.tmpl +++ b/cmd/documentation/exchanges_templates/orders.tmpl @@ -7,6 +7,8 @@ - Deletion of order - Order tracking ++ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders, keeping a history of orders and tracking unrealised & realised PNL + ### Please click GoDocs chevron above to view current GoDoc information for this package {{template "contributions"}} {{template "donations" .}} diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index eef898fad87..16391e66655 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4776,6 +4776,11 @@ var getFuturesPositionsCommand = &cli.Command{ Aliases: []string{"v"}, Usage: "includes all orders that make up a position in the response", }, + &cli.BoolFlag{ + Name: "overwrite", + Aliases: []string{"o"}, + Usage: "if true, will overwrite futures results for the provided exchange, asset, pair", + }, }, } @@ -4862,6 +4867,16 @@ func getFuturesPositions(c *cli.Context) error { } } + var overwrite bool + if c.IsSet("overwrite") { + overwrite = c.Bool("overwrite") + } else if c.Args().Get(2) != "" { + overwrite, err = strconv.ParseBool(c.Args().Get(2)) + if err != nil { + return err + } + } + var s, e time.Time s, err = time.Parse(common.SimpleTimeFormat, startTime) if err != nil { @@ -4897,6 +4912,7 @@ func getFuturesPositions(c *cli.Context) error { Status: status, PositionLimit: int64(limit), Verbose: verbose, + Overwrite: overwrite, }) if err != nil { return err diff --git a/engine/order_manager.go b/engine/order_manager.go index e4731cd525a..b64ee558116 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -225,8 +225,6 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { return nil } -var errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") - // GetFuturesPositionsForExchange returns futures positions stored within // the order manager's futures position tracker that match the provided params func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*order.PositionTracker, error) { @@ -246,6 +244,25 @@ func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.It return m.orderStore.futuresPositionController.GetPositionsForExchange(exch, item, pair) } +// ClearFuturesTracking will clear existing futures positions for a given exchange, +// asset, pair for the event that positions have not been tracked accurately +func (m *OrderManager) ClearFuturesTracking(exch string, item asset.Item, pair currency.Pair) error { + if m == nil { + return fmt.Errorf("order manager %w", ErrNilSubsystem) + } + if atomic.LoadInt32(&m.started) == 0 { + return fmt.Errorf("order manager %w", ErrSubSystemNotStarted) + } + if m.orderStore.futuresPositionController == nil { + return errFuturesTrackerNotSetup + } + if !item.IsFutures() { + return fmt.Errorf("%v %w", item, order.ErrNotFutureAsset) + } + + return m.orderStore.futuresPositionController.ClearPositionsForExchange(exch, item, pair) +} + // GetOrderInfo calls the exchange's wrapper GetOrderInfo function // and stores the result in the order manager func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID string, cp currency.Pair, a asset.Item) (order.Detail, error) { @@ -519,8 +536,6 @@ func (m *OrderManager) GetOrdersActive(f *order.Filter) ([]order.Detail, error) return m.orderStore.getActiveOrders(f), nil } -var errUnableToPlaceOrder = errors.New("cannot process order, order not placed") - // processSubmittedOrder adds a new order to the manager func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result order.SubmitResponse) (*OrderSubmitResponse, error) { if !result.IsOrderPlaced { diff --git a/engine/order_manager.md b/engine/order_manager.md index 49b82cf101c..bb0161f3636 100644 --- a/engine/order_manager.md +++ b/engine/order_manager.md @@ -22,6 +22,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader + The order manager subsystem stores and monitors all orders from enabled exchanges with API keys and `authenticatedSupport` enabled + It can be enabled or disabled via runtime command `-ordermanager=false` and defaults to true + All orders placed via GoCryptoTrader will be added to the order manager store ++ Any futures based order will be tracked via the [futures positions controller](/exchanges/orders/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ### Please click GoDocs chevron above to view current GoDoc information for this package diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 668253db351..78b9581a40d 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -1184,6 +1184,35 @@ func TestGetFuturesPositionsForExchange(t *testing.T) { } } +func TestClearFuturesPositionsForExchange(t *testing.T) { + t.Parallel() + o := &OrderManager{} + cp := currency.NewPair(currency.BTC, currency.USDT) + err := o.ClearFuturesTracking("test", asset.Spot, cp) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + err = o.ClearFuturesTracking("test", asset.Spot, cp) + if !errors.Is(err, errFuturesTrackerNotSetup) { + t.Errorf("received '%v', expected '%v'", err, errFuturesTrackerNotSetup) + } + o.orderStore.futuresPositionController = order.SetupPositionController() + err = o.ClearFuturesTracking("test", asset.Spot, cp) + if !errors.Is(err, order.ErrNotFutureAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFutureAsset) + } + + err = o.ClearFuturesTracking("test", asset.Futures, cp) + if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) + } + o = nil + err = o.ClearFuturesTracking("test", asset.Futures, cp) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } +} func TestSubmitFakeOrder(t *testing.T) { t.Parallel() o := &OrderManager{} diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index 50dcbae5e27..b8166fd594e 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -14,15 +14,19 @@ const OrderManagerName = "orders" // vars for the fund manager package var ( - orderManagerDelay = time.Second * 10 // ErrOrdersAlreadyExists occurs when the order already exists in the manager ErrOrdersAlreadyExists = errors.New("order already exists") - // ErrOrderNotFound occurs when an order is not found in the orderstore - ErrOrderNotFound = errors.New("order does not exist") - errNilCommunicationsManager = errors.New("cannot start with nil communications manager") // ErrOrderIDCannotBeEmpty occurs when an order does not have an ID ErrOrderIDCannotBeEmpty = errors.New("orderID cannot be empty") - errNilOrder = errors.New("nil order received") + // ErrOrderNotFound occurs when an order is not found in the orderstore + ErrOrderNotFound = errors.New("order does not exist") + + errNilCommunicationsManager = errors.New("cannot start with nil communications manager") + errNilOrder = errors.New("nil order received") + errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") + errUnableToPlaceOrder = errors.New("cannot process order, order not placed") + + orderManagerDelay = time.Second * 10 ) type orderManagerConfig struct { diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 394ec52d795..cd89a1d614a 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4150,6 +4150,12 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture sort.Slice(orders, func(i, j int) bool { return orders[i].Date.Before(orders[j].Date) }) + if r.Overwrite { + err = s.OrderManager.ClearFuturesTracking(r.Exchange, a, cp) + if err != nil { + return nil, err + } + } for i := range orders { _, err = s.OrderManager.UpsertOrder(&orders[i]) if err != nil { diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index aa34fc525a3..1ffbb105ee0 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1641,23 +1641,23 @@ func (c CollateralWeightHolder) isLoaded() bool { } func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { - butts, ok := c[code] + currencyCollateral, ok := c[code] if !ok { - butts = CollateralWeight{Total: weighting} + currencyCollateral = CollateralWeight{Total: weighting} } else { - butts.Total = weighting + currencyCollateral.Total = weighting } - c[code] = butts + c[code] = currencyCollateral } func (c CollateralWeightHolder) loadIMF(code string, imf float64) { - butts, ok := c[code] + currencyCollateral, ok := c[code] if !ok { - butts = CollateralWeight{IMFFactor: imf} + currencyCollateral = CollateralWeight{IMFFactor: imf} } else { - butts.IMFFactor = imf + currencyCollateral.IMFFactor = imf } - c[code] = butts + c[code] = currencyCollateral } func (c CollateralWeightHolder) load(code string, initial, total, imfFactor float64) { diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 8c9d0ed110e..1c1d1d37628 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -268,7 +268,6 @@ func TestGetPositions(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - f.Verbose = true _, err := f.GetPositions(context.Background()) if err != nil { t.Error(err) @@ -734,7 +733,6 @@ func TestGetFills(t *testing.T) { t.Skip() } // optional params - _, err := f.GetFills(context.Background(), spotPair, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) @@ -1589,6 +1587,7 @@ func TestSubaccountBalances(t *testing.T) { } func TestSubaccountTransfer(t *testing.T) { + t.Parallel() tt := []struct { Coin currency.Code Source string @@ -1618,6 +1617,7 @@ func TestSubaccountTransfer(t *testing.T) { } func TestGetStakes(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1628,6 +1628,7 @@ func TestGetStakes(t *testing.T) { } func TestGetUnstakeRequests(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1638,6 +1639,7 @@ func TestGetUnstakeRequests(t *testing.T) { } func TestGetStakeBalances(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1648,6 +1650,7 @@ func TestGetStakeBalances(t *testing.T) { } func TestUnstakeRequest(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set") } @@ -1663,6 +1666,7 @@ func TestUnstakeRequest(t *testing.T) { } func TestCancelUnstakeRequest(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set") } @@ -1673,6 +1677,7 @@ func TestCancelUnstakeRequest(t *testing.T) { } func TestGetStakingRewards(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1683,6 +1688,7 @@ func TestGetStakingRewards(t *testing.T) { } func TestStakeRequest(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set") } @@ -1695,6 +1701,7 @@ func TestStakeRequest(t *testing.T) { } func TestUpdateOrderExecutionLimits(t *testing.T) { + t.Parallel() err := f.UpdateOrderExecutionLimits(context.Background(), "") if err != nil { t.Fatal(err) @@ -1721,6 +1728,7 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { } func TestScaleCollateral(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1797,6 +1805,7 @@ func TestScaleCollateral(t *testing.T) { } func TestCalculateTotalCollateral(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1858,10 +1867,10 @@ func TestCalculateTotalCollateral(t *testing.T) { } func TestCalculatePNL(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - f.Verbose = true pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) positions, err := f.GetFuturesPositions(context.Background(), asset.Futures, pair, time.Date(2021, 1, 6, 4, 28, 0, 0, time.UTC), time.Date(2021, 12, 31, 4, 32, 0, 0, time.UTC)) if err != nil { @@ -1912,6 +1921,7 @@ func TestCalculatePNL(t *testing.T) { } func TestGetFuturesPositions(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1924,3 +1934,28 @@ func TestGetFuturesPositions(t *testing.T) { t.Error(err) } } + +func TestLoadCollateralWeightings(t *testing.T) { + t.Parallel() + ff := FTX{} + err := ff.LoadCollateralWeightings() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(ff.collateralWeight) == 0 { + t.Fatal("expected some weight") + } + if !ff.collateralWeight.isLoaded() { + t.Error("expected loaded weight") + } + if !areTestAPIKeysSet() { + return + } + err = f.LoadCollateralWeightings() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(f.collateralWeight) == 0 { + t.Fatal("expected some weight") + } +} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b8f87b4f97d..b3b05121608 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -81,9 +81,49 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite if !ok { return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) } + return multiPositionTracker.GetPositions(), nil } +// ClearPositionsForExchange resets positions for an +// exchange, asset, pair that has been stored +func (c *PositionController) ClearPositionsForExchange(exch string, item asset.Item, pair currency.Pair) error { + if c == nil { + return common.ErrNilPointer + } + c.m.Lock() + defer c.m.Unlock() + if !item.IsFutures() { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFutureAsset) + } + exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + if !ok { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) + } + itemM, ok := exchM[item] + if !ok { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForAsset) + } + multiPositionTracker, ok := itemM[pair] + if !ok { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) + } + newMPT, err := SetupMultiPositionTracker(&MultiPositionTrackerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: multiPositionTracker.underlying, + OfflineCalculation: multiPositionTracker.offlinePNLCalculation, + UseExchangePNLCalculation: multiPositionTracker.useExchangePNLCalculations, + ExchangePNLCalculation: multiPositionTracker.exchangePNLCalculation, + }) + if err != nil { + return err + } + c.positionTrackerControllers[strings.ToLower(exch)][item][pair] = newMPT + return nil +} + // SetupMultiPositionTracker creates a futures order tracker for a specific exchange func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPositionTracker, error) { if setup == nil { @@ -397,9 +437,10 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { cal.Exposure.LessThan(amount) { // latest order swaps directions! // split the order to calculate PNL from each direction - first := amount.Sub(cal.Exposure) - second := cal.Exposure.Sub(amount).Abs() - cal.Fee = cal.Fee.Div(decimal.NewFromInt(2)) + first := cal.Exposure + second := amount.Sub(cal.Exposure) + baseFee := cal.Fee.Div(amount) + cal.Fee = baseFee.Mul(first) cal.Amount = first result, err = p.PNLCalculation.CalculatePNL(cal) if err != nil { @@ -420,6 +461,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.openingDirection = Long } + cal.Fee = baseFee.Mul(second) cal.Amount = second cal.EntryPrice = price cal.Time = cal.Time.Add(1) @@ -475,9 +517,6 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { // CalculatePNL this is a localised generic way of calculating open // positions' worth, it is an implementation of the PNLCalculation interface -// -// do not use any properties of p, use calc, otherwise there will be -// sync issues func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, error) { if calc == nil { return nil, ErrNilPNLCalculator @@ -536,6 +575,8 @@ func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, er return response, nil } +// calculateRealisedPNL calculates the total realised PNL +// based on PNL history, minus fees func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 3ed079969e8..69553a0da8a 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -125,6 +125,7 @@ func TestTrackNewOrder(t *testing.T) { t.Error("expected 1") } + od.Date = od.Date.Add(1) od.Amount = 0.4 od.Side = Short od.ID = "3" @@ -141,9 +142,12 @@ func TestTrackNewOrder(t *testing.T) { if f.exposure.InexactFloat64() != 0.6 { t.Error("expected 0.6") } + + od.Date = od.Date.Add(1) od.Amount = 0.8 od.Side = Short od.ID = "4" + od.Fee = 0.1 err = f.TrackNewOrder(od) if !errors.Is(err, nil) { t.Error(err) @@ -155,6 +159,7 @@ func TestTrackNewOrder(t *testing.T) { t.Errorf("expected %v received %v", 0.2, f.exposure) } + od.Date = od.Date.Add(1) od.ID = "5" od.Side = Long od.Amount = 0.2 @@ -522,6 +527,58 @@ func TestGetPositionsForExchange(t *testing.T) { } } +func TestClearPositionsForExchange(t *testing.T) { + t.Parallel() + c := &PositionController{} + p := currency.NewPair(currency.BTC, currency.USDT) + err := c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange] = nil + err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForAsset) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + c.positionTrackerControllers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures] = nil + err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForPair) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) + } + err = c.ClearPositionsForExchange(testExchange, asset.Spot, p) + if !errors.Is(err, ErrNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + } + + c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, + } + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, + underlying: currency.DOGE, + positions: []*PositionTracker{ + { + exchange: testExchange, + }, + }, + } + err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if len(c.positionTrackerControllers[testExchange][asset.Futures][p].positions) != 0 { + t.Fatal("expected 0") + } + c = nil + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } +} + func TestCalculateRealisedPNL(t *testing.T) { t.Parallel() result := calculateRealisedPNL(nil) diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 7ef71ba2121..ddef4eaffd2 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -10778,6 +10778,7 @@ type GetFuturesPositionsRequest struct { Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` Verbose bool `protobuf:"varint,8,opt,name=verbose,proto3" json:"verbose,omitempty"` + Overwrite bool `protobuf:"varint,9,opt,name=overwrite,proto3" json:"overwrite,omitempty"` } func (x *GetFuturesPositionsRequest) Reset() { @@ -10868,6 +10869,13 @@ func (x *GetFuturesPositionsRequest) GetVerbose() bool { return false } +func (x *GetFuturesPositionsRequest) GetOverwrite() bool { + if x != nil { + return x.Overwrite + } + return false +} + type GetFuturesPositionsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -12779,7 +12787,7 @@ var file_rpc_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x1a, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa8, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, @@ -12796,785 +12804,787 @@ var file_rpc_proto_rawDesc = []byte{ 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, - 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, - 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, - 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, - 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, - 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, - 0x4e, 0x4c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, - 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x24, 0x0a, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, - 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, - 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, - 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, - 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, - 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, - 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, - 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, - 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, - 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x12, 0x4b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, - 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, - 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, - 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, - 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, - 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, - 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, - 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, - 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, - 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, - 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, - 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, - 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, + 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, + 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, + 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, + 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, + 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, + 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, + 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, + 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, + 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, + 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, + 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, + 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, + 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2a, + 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, + 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x4b, + 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, + 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, 0x0a, 0x15, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, + 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, + 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, + 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, + 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, + 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, + 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, + 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, + 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, - 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, + 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, + 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, - 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, - 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, - 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, - 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, - 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, + 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, - 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, - 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, + 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, + 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, + 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, + 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, + 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, - 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, - 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, - 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, - 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, - 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, - 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, - 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, + 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, + 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, + 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, + 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, + 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, + 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, - 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, - 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, - 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, - 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, + 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, + 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, + 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, + 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, - 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, - 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, - 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, - 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, + 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, + 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, + 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, - 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, - 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, + 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, + 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, + 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, - 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, - 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, + 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, + 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, + 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, + 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, + 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, - 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, - 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, - 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, + 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, + 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, + 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, + 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, + 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, + 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, - 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, - 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, + 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, + 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 601cedad4bf..86b123a51fb 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1032,6 +1032,7 @@ message GetFuturesPositionsRequest { string status = 6; int64 positionLimit = 7; bool verbose = 8; + bool overwrite = 9; } message GetFuturesPositionsResponse { diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index a336d1b6f03..8f58bcd38d0 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1837,6 +1837,12 @@ "in": "query", "required": false, "type": "boolean" + }, + { + "name": "overwrite", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ From 10cedd3ed5e0ffb220e1ca1edb1dc5846260b58d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 7 Jan 2022 13:25:17 +1100 Subject: [PATCH 004/171] Test protection if someone has zero collateral --- exchanges/ftx/ftx_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 1c1d1d37628..e34235e3e6f 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1799,6 +1799,9 @@ func TestScaleCollateral(t *testing.T) { } } } + if accountInfo.Collateral == 0 { + return + } if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } @@ -1853,7 +1856,7 @@ func TestCalculateTotalCollateral(t *testing.T) { if err != nil { t.Error(err) } - if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { + if accountInfo.Collateral != 0 && (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral)*100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } From bc67a86c3fc9f546f4feb6ae3e5bf64952c55c02 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 10 Jan 2022 08:43:26 +1100 Subject: [PATCH 005/171] Uses string instead of double for accuracy --- .golangci.yml | 2 +- engine/rpcserver.go | 34 ++++++++++++++++++++++++------ exchanges/account/account_types.go | 2 +- exchanges/ftx/ftx_wrapper.go | 14 +++++++----- gctrpc/rpc.pb.go | 24 ++++++++++----------- gctrpc/rpc.proto | 6 +++--- gctrpc/rpc.swagger.json | 9 +++----- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3adb168a10b..732bd8c0ea7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - timeout: 3m + timeout: 6m issues-exit-code: 1 tests: true skip-dirs: diff --git a/engine/rpcserver.go b/engine/rpcserver.go index cd89a1d614a..b36d68afdde 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4169,6 +4169,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture return nil, err } response := &gctrpc.GetFuturesPositionsResponse{} + var totalRealisedPNL, totalUnrealisedPNL decimal.Decimal for i := range pos { if r.PositionLimit > 0 && len(response.Positions) >= int(r.PositionLimit) { break @@ -4176,10 +4177,18 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture stats := pos[i].GetStats() response.TotalOrders += int64(len(stats.Orders)) details := &gctrpc.FuturePosition{ - Status: stats.Status.String(), - CurrentDirection: stats.LatestDirection.String(), - UnrealisedPNL: stats.UnrealisedPNL.String(), - RealisedPNL: stats.RealisedPNL.String(), + Status: stats.Status.String(), + UnrealisedPNL: stats.UnrealisedPNL.String(), + RealisedPNL: stats.RealisedPNL.String(), + } + if !stats.UnrealisedPNL.IsZero() { + details.UnrealisedPNL = stats.UnrealisedPNL.String() + } + if !stats.RealisedPNL.IsZero() { + details.RealisedPNL = stats.RealisedPNL.String() + } + if stats.LatestDirection != order.UnknownSide { + details.CurrentDirection = stats.LatestDirection.String() } if len(stats.PNLHistory) > 0 { details.OpeningDate = stats.PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) @@ -4187,8 +4196,8 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture details.ClosingDate = stats.PNLHistory[len(stats.PNLHistory)-1].Time.Format(common.SimpleTimeFormatWithTimezone) } } - response.TotalRealisedPNL += stats.RealisedPNL.InexactFloat64() - response.TotalUnrealisedPNL += stats.UnrealisedPNL.InexactFloat64() + totalRealisedPNL = totalRealisedPNL.Add(stats.RealisedPNL) + totalUnrealisedPNL = totalUnrealisedPNL.Add(stats.UnrealisedPNL) if !r.Verbose { response.Positions = append(response.Positions, details) continue @@ -4229,7 +4238,15 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture } response.Positions = append(response.Positions, details) } - response.TotalPNL = response.TotalRealisedPNL + response.TotalUnrealisedPNL + if !totalRealisedPNL.Add(totalUnrealisedPNL).IsZero() { + response.TotalPNL = totalRealisedPNL.Add(totalUnrealisedPNL).String() + } + if !totalUnrealisedPNL.IsZero() { + response.TotalUnrealisedPNL = totalUnrealisedPNL.String() + } + if !totalRealisedPNL.IsZero() { + response.TotalRealisedPNL = totalRealisedPNL.String() + } return response, nil } @@ -4265,6 +4282,9 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe acc = ai.Accounts[0] } for i := range acc.Currencies { + if acc.Currencies[i].TotalValue == 0 { + continue + } calculators = append(calculators, order.CollateralCalculator{ CalculateOffline: r.CalculateOffline, CollateralCurrency: acc.Currencies[i].CurrencyName, diff --git a/exchanges/account/account_types.go b/exchanges/account/account_types.go index 7e1b2339b9d..682b8a03a94 100644 --- a/exchanges/account/account_types.go +++ b/exchanges/account/account_types.go @@ -34,7 +34,7 @@ type Holdings struct { Accounts []SubAccount } -// SubAccount defines a singular account type with asocciated currency balances +// SubAccount defines a singular account type with associated currency balances type SubAccount struct { ID string AssetType asset.Item diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 8eace2ef2b1..6eb04be16f6 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1376,11 +1376,15 @@ func (f *FTX) CalculateTotalCollateral(ctx context.Context, collateralAssets []o return nil, err } result.TotalCollateral = result.TotalCollateral.Add(collateral) - result.BreakdownByCurrency = append(result.BreakdownByCurrency, order.CollateralByCurrency{ - Currency: collateralAssets[i].CollateralCurrency, - Amount: collateral, - ValueCurrency: currency.USD, - }) + curr := order.CollateralByCurrency{ + Currency: collateralAssets[i].CollateralCurrency, + Amount: collateral, + } + if collateralAssets[i].CollateralCurrency != currency.USD { + curr.ValueCurrency = currency.USD + } + + result.BreakdownByCurrency = append(result.BreakdownByCurrency, curr) } return &result, nil } diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index ddef4eaffd2..3e0aeec4f7d 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -10882,9 +10882,9 @@ type GetFuturesPositionsResponse struct { unknownFields protoimpl.UnknownFields TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` - TotalRealisedPNL float64 `protobuf:"fixed64,2,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` - TotalUnrealisedPNL float64 `protobuf:"fixed64,3,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` - TotalPNL float64 `protobuf:"fixed64,4,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` + TotalRealisedPNL string `protobuf:"bytes,2,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` + TotalUnrealisedPNL string `protobuf:"bytes,3,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` + TotalPNL string `protobuf:"bytes,4,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` Positions []*FuturePosition `protobuf:"bytes,5,rep,name=positions,proto3" json:"positions,omitempty"` } @@ -10927,25 +10927,25 @@ func (x *GetFuturesPositionsResponse) GetTotalOrders() int64 { return 0 } -func (x *GetFuturesPositionsResponse) GetTotalRealisedPNL() float64 { +func (x *GetFuturesPositionsResponse) GetTotalRealisedPNL() string { if x != nil { return x.TotalRealisedPNL } - return 0 + return "" } -func (x *GetFuturesPositionsResponse) GetTotalUnrealisedPNL() float64 { +func (x *GetFuturesPositionsResponse) GetTotalUnrealisedPNL() string { if x != nil { return x.TotalUnrealisedPNL } - return 0 + return "" } -func (x *GetFuturesPositionsResponse) GetTotalPNL() float64 { +func (x *GetFuturesPositionsResponse) GetTotalPNL() string { if x != nil { return x.TotalPNL } - return 0 + return "" } func (x *GetFuturesPositionsResponse) GetPositions() []*FuturePosition { @@ -12812,12 +12812,12 @@ var file_rpc_proto_rawDesc = []byte{ 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x28, 0x09, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, - 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, + 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 86b123a51fb..1b2e2a6c453 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1037,9 +1037,9 @@ message GetFuturesPositionsRequest { message GetFuturesPositionsResponse { int64 totalOrders = 1; - double totalRealisedPNL = 2; - double totalUnrealisedPNL = 3; - double totalPNL = 4; + string totalRealisedPNL = 2; + string totalUnrealisedPNL = 3; + string totalPNL = 4; repeated FuturePosition positions = 5; } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 8f58bcd38d0..4910b48053f 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -4391,16 +4391,13 @@ "format": "int64" }, "totalRealisedPNL": { - "type": "number", - "format": "double" + "type": "string" }, "totalUnrealisedPNL": { - "type": "number", - "format": "double" + "type": "string" }, "totalPNL": { - "type": "number", - "format": "double" + "type": "string" }, "positions": { "type": "array", From 025452cf7142b9ac030218d1d7ab9c0f4c27c7e1 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 10 Jan 2022 08:58:01 +1100 Subject: [PATCH 006/171] Fixes old code panic --- cmd/apichecker/apicheck.go | 4 +++- engine/rpcserver.go | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/apichecker/apicheck.go b/cmd/apichecker/apicheck.go index 12eaaa38353..8d6850191ef 100644 --- a/cmd/apichecker/apicheck.go +++ b/cmd/apichecker/apicheck.go @@ -848,7 +848,9 @@ loop: } } } - resp = resp[:1] + if len(resp) > 1 { + resp = resp[:1] + } return resp, nil } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index b36d68afdde..6f917145fb7 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4238,15 +4238,16 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture } response.Positions = append(response.Positions, details) } - if !totalRealisedPNL.Add(totalUnrealisedPNL).IsZero() { - response.TotalPNL = totalRealisedPNL.Add(totalUnrealisedPNL).String() - } + if !totalUnrealisedPNL.IsZero() { response.TotalUnrealisedPNL = totalUnrealisedPNL.String() } if !totalRealisedPNL.IsZero() { response.TotalRealisedPNL = totalRealisedPNL.String() } + if !totalUnrealisedPNL.IsZero() && !totalRealisedPNL.IsZero() { + response.TotalPNL = totalRealisedPNL.Add(totalUnrealisedPNL).String() + } return response, nil } From d8bb68887e9ab66b66dc96ff875b61c2ce7bc9c1 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 10 Jan 2022 15:43:02 +1100 Subject: [PATCH 007/171] context, match, docs --- .../engine_templates/order_manager.tmpl | 2 +- .../exchanges_templates/orders.tmpl | 2 +- cmd/gctcli/commands.go | 12 +- engine/order_manager.md | 2 +- exchanges/asset/asset_test.go | 67 +++++++++++ exchanges/coinbasepro/coinbasepro.go | 4 +- exchanges/coinut/coinut.go | 2 +- exchanges/exchange.go | 2 +- exchanges/exchange_test.go | 84 ++++++++++++++ exchanges/ftx/ftx.go | 23 ++-- exchanges/ftx/ftx_test.go | 19 +++- exchanges/ftx/ftx_types.go | 105 +++++++++--------- exchanges/ftx/ftx_wrapper.go | 47 ++++---- exchanges/itbit/itbit.go | 2 +- exchanges/order/README.md | 19 ++-- exchanges/order/futures.go | 9 +- exchanges/order/futures_test.go | 16 +-- exchanges/order/futures_types.go | 2 +- exchanges/yobit/yobit.go | 6 +- 19 files changed, 300 insertions(+), 125 deletions(-) diff --git a/cmd/documentation/engine_templates/order_manager.tmpl b/cmd/documentation/engine_templates/order_manager.tmpl index 1aed6f81dcf..4c36e259088 100644 --- a/cmd/documentation/engine_templates/order_manager.tmpl +++ b/cmd/documentation/engine_templates/order_manager.tmpl @@ -4,7 +4,7 @@ + The order manager subsystem stores and monitors all orders from enabled exchanges with API keys and `authenticatedSupport` enabled + It can be enabled or disabled via runtime command `-ordermanager=false` and defaults to true + All orders placed via GoCryptoTrader will be added to the order manager store -+ Any futures based order will be tracked via the [futures positions controller](/exchanges/orders/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ++ Any futures based order will be tracked via the [futures positions controller](/exchanges/order/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ### Please click GoDocs chevron above to view current GoDoc information for this package {{template "contributions"}} diff --git a/cmd/documentation/exchanges_templates/orders.tmpl b/cmd/documentation/exchanges_templates/orders.tmpl index 78bd5db115b..a960bbdbd8c 100644 --- a/cmd/documentation/exchanges_templates/orders.tmpl +++ b/cmd/documentation/exchanges_templates/orders.tmpl @@ -1,4 +1,4 @@ -{{define "exchanges orders" -}} +{{define "exchanges order" -}} {{template "header" .}} ## Current Features for {{.Name}} diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 16391e66655..3f0a4749742 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -17,7 +17,7 @@ import ( "github.com/urfave/cli/v2" ) -var startTime, endTime, o string +var startTime, endTime, orderingDirection string var limit int var getInfoCommand = &cli.Command{ @@ -3742,7 +3742,7 @@ var getAuditEventCommand = &cli.Command{ Aliases: []string{"o"}, Usage: "order results by ascending/descending", Value: "asc", - Destination: &o, + Destination: &orderingDirection, }, &cli.IntFlag{ Name: "limit", @@ -3769,7 +3769,7 @@ func getAuditEvent(c *cli.Context) error { if !c.IsSet("order") { if c.Args().Get(2) != "" { - o = c.Args().Get(2) + orderingDirection = c.Args().Get(2) } } @@ -3810,7 +3810,7 @@ func getAuditEvent(c *cli.Context) error { StartDate: negateLocalOffset(s), EndDate: negateLocalOffset(e), Limit: int32(limit), - OrderBy: o, + OrderBy: orderingDirection, }) if err != nil { @@ -4726,7 +4726,7 @@ func findMissingSavedCandleIntervals(c *cli.Context) error { var getFuturesPositionsCommand = &cli.Command{ Name: "getfuturesposition", Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened", - ArgsUsage: " ", + ArgsUsage: " ", Action: getFuturesPositions, Flags: []cli.Flag{ &cli.StringFlag{ @@ -4925,7 +4925,7 @@ func getFuturesPositions(c *cli.Context) error { var getCollateralCommand = &cli.Command{ Name: "getcollateral", Usage: "returns total collateral for an exchange asset, with optional per currency breakdown", - ArgsUsage: " ", + ArgsUsage: " ", Action: getCollateral, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/engine/order_manager.md b/engine/order_manager.md index bb0161f3636..3ecac298197 100644 --- a/engine/order_manager.md +++ b/engine/order_manager.md @@ -22,7 +22,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader + The order manager subsystem stores and monitors all orders from enabled exchanges with API keys and `authenticatedSupport` enabled + It can be enabled or disabled via runtime command `-ordermanager=false` and defaults to true + All orders placed via GoCryptoTrader will be added to the order manager store -+ Any futures based order will be tracked via the [futures positions controller](/exchanges/orders/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ++ Any futures based order will be tracked via the [futures positions controller](/exchanges/order/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ### Please click GoDocs chevron above to view current GoDoc information for this package diff --git a/exchanges/asset/asset_test.go b/exchanges/asset/asset_test.go index 2bf7cf630ed..9f274d3fad6 100644 --- a/exchanges/asset/asset_test.go +++ b/exchanges/asset/asset_test.go @@ -87,3 +87,70 @@ func TestSupported(t *testing.T) { } } } + +func TestIsFutures(t *testing.T) { + t.Parallel() + type scenario struct { + item Item + isFutures bool + } + scenarios := []scenario{ + { + item: Spot, + isFutures: false, + }, + { + item: Margin, + isFutures: false, + }, + { + item: MarginFunding, + isFutures: false, + }, + { + item: Index, + isFutures: false, + }, + { + item: Binary, + isFutures: false, + }, + { + item: PerpetualContract, + isFutures: true, + }, + { + item: PerpetualSwap, + isFutures: true, + }, + { + item: Futures, + isFutures: true, + }, + { + item: UpsideProfitContract, + isFutures: true, + }, + { + item: DownsideProfitContract, + isFutures: true, + }, + { + item: CoinMarginedFutures, + isFutures: true, + }, + { + item: USDTMarginedFutures, + isFutures: true, + }, + } + for _, s := range scenarios { + testScenario := s + t.Run(testScenario.item.String(), func(t *testing.T) { + t.Parallel() + if testScenario.item.IsFutures() != testScenario.isFutures { + t.Errorf("expected %v isFutures to be %v", testScenario.item, testScenario.isFutures) + } + }) + } +} diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index cc2cdb8b4d5..c53071dc1d8 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -810,7 +810,7 @@ func (c *CoinbasePro) calculateTradingFee(trailingVolume []Volume, base, quote c func getInternationalBankWithdrawalFee(c currency.Code) float64 { var fee float64 - if c == currency.USD { + if c.Match(currency.USD) { fee = 25 } else if c == currency.EUR { fee = 0.15 @@ -822,7 +822,7 @@ func getInternationalBankWithdrawalFee(c currency.Code) float64 { func getInternationalBankDepositFee(c currency.Code) float64 { var fee float64 - if c == currency.USD { + if c.Match(currency.USD) { fee = 10 } else if c == currency.EUR { fee = 0.15 diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 4670e6cddac..4b278fcce03 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -403,7 +403,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, amount float64) float64 func getInternationalBankDepositFee(c currency.Code, amount float64) float64 { var fee float64 - if c == currency.USD { + if c.Match(currency.USD) { if amount*0.001 < 10 { fee = 10 } else { diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 0c2d1c127f9..e793b9c2def 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1463,7 +1463,7 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ // It will also determine whether the position is considered to be liquidated // For live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset -func (b *Base) CalculatePNL(*order.PNLCalculatorRequest) (*order.PNLResult, error) { +func (b *Base) CalculatePNL(context.Context, *order.PNLCalculatorRequest) (*order.PNLResult, error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 80ffd6fc4fb..0c210e598b8 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -2464,3 +2464,87 @@ func TestGetAvailableTransferChains(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrFunctionNotSupported) } } + +func TestCalculatePNL(t *testing.T) { + t.Parallel() + var b Base + if _, err := b.CalculatePNL(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) { + t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) + } +} + +func TestScaleCollateral(t *testing.T) { + t.Parallel() + var b Base + if _, err := b.ScaleCollateral(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) { + t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) + } +} + +func TestCalculateTotalCollateral(t *testing.T) { + t.Parallel() + var b Base + if _, err := b.CalculateTotalCollateral(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) { + t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) + } +} + +func TestGetFuturesPositions(t *testing.T) { + t.Parallel() + var b Base + if _, err := b.GetFuturesPositions(context.Background(), asset.Spot, currency.Pair{}, time.Time{}, time.Time{}); !errors.Is(err, common.ErrNotYetImplemented) { + t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) + } +} + +func TestUpdateCurrencyStates(t *testing.T) { + t.Parallel() + var b Base + if err := b.UpdateCurrencyStates(context.Background(), asset.Spot); !errors.Is(err, common.ErrNotYetImplemented) { + t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) + } +} + +func TestUpdateOrderExecutionLimits(t *testing.T) { + t.Parallel() + var b Base + if err := b.UpdateOrderExecutionLimits(context.Background(), asset.Spot); !errors.Is(err, common.ErrNotYetImplemented) { + t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) + } +} + +func TestSetTradeFeedStatus(t *testing.T) { + t.Parallel() + b := Base{ + Config: &config.Exchange{ + Features: &config.FeaturesConfig{}, + }, + Verbose: true, + } + b.SetTradeFeedStatus(true) + if !b.IsTradeFeedEnabled() { + t.Error("expected true") + } + b.SetTradeFeedStatus(false) + if b.IsTradeFeedEnabled() { + t.Error("expected false") + } +} + +func TestSetFillsFeedStatus(t *testing.T) { + t.Parallel() + b := Base{ + Config: &config.Exchange{ + Features: &config.FeaturesConfig{}, + }, + Verbose: true, + } + b.SetFillsFeedStatus(true) + if !b.IsFillsFeedEnabled() { + t.Error("expected true") + } + b.SetFillsFeedStatus(false) + if b.IsFillsFeedEnabled() { + t.Error("expected false") + } +} diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 1ffbb105ee0..f76fdbfae1d 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -141,7 +141,7 @@ var ( errUnrecognisedOrderStatus = errors.New("unrecognised order status received") errInvalidOrderAmounts = errors.New("filled amount should not exceed order amount") errCollateralCurrencyNotFound = errors.New("no collateral scaling information found") - errCollateralIMFMissing = errors.New("cannot scale collateral, missing IMF information") + errCollateralInitialMarginFractionMissing = errors.New("cannot scale collateral, missing initial margin fraction information") validResolutionData = []int64{15, 60, 300, 900, 3600, 14400, 86400} ) @@ -842,13 +842,13 @@ func (f *FTX) DeleteTriggerOrder(ctx context.Context, orderID string) (string, e } // GetFills gets fills' data -func (f *FTX) GetFills(ctx context.Context, market currency.Pair, limit string, startTime, endTime time.Time) ([]FillsData, error) { +func (f *FTX) GetFills(ctx context.Context, market currency.Pair, item asset.Item, limit string, startTime, endTime time.Time) ([]FillsData, error) { resp := struct { Data []FillsData `json:"result"` }{} params := url.Values{} if !market.IsEmpty() { - fp, err := f.FormatExchangeCurrency(market, asset.Futures) + fp, err := f.FormatExchangeCurrency(market, item) if err != nil { return nil, err } @@ -1478,7 +1478,7 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err // LoadCollateralWeightings sets the collateral weights for // currencies supported by FTX -func (f *FTX) LoadCollateralWeightings() error { +func (f *FTX) LoadCollateralWeightings(ctx context.Context) error { f.collateralWeight = make(map[string]CollateralWeight) // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral // sets default, then uses the latest from FTX @@ -1613,7 +1613,6 @@ func (f *FTX) LoadCollateralWeightings() error { if !f.GetAuthenticatedAPISupport(exchange.RestAuthentication) { return nil } - ctx := context.Background() coins, err := f.GetCoins(ctx) if err != nil { return err @@ -1630,7 +1629,7 @@ func (f *FTX) LoadCollateralWeightings() error { return err } for i := range futures { - f.collateralWeight.loadIMF(futures[i].Underlying, futures[i].IMFFactor) + f.collateralWeight.loadInitialMarginFraction(futures[i].Underlying, futures[i].InitialMarginFractionFactor) } return nil @@ -1650,20 +1649,20 @@ func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { c[code] = currencyCollateral } -func (c CollateralWeightHolder) loadIMF(code string, imf float64) { +func (c CollateralWeightHolder) loadInitialMarginFraction(code string, imf float64) { currencyCollateral, ok := c[code] if !ok { - currencyCollateral = CollateralWeight{IMFFactor: imf} + currencyCollateral = CollateralWeight{InitialMarginFractionFactor: imf} } else { - currencyCollateral.IMFFactor = imf + currencyCollateral.InitialMarginFractionFactor = imf } c[code] = currencyCollateral } func (c CollateralWeightHolder) load(code string, initial, total, imfFactor float64) { c[code] = CollateralWeight{ - Initial: initial, - Total: total, - IMFFactor: imfFactor, + Initial: initial, + Total: total, + InitialMarginFractionFactor: imfFactor, } } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index e34235e3e6f..a76db906095 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -236,6 +236,13 @@ func TestGetFutureStats(t *testing.T) { if err != nil { t.Error(err) } + future, err := f.GetFutureStats(context.Background(), "BTC-MOVE-2021Q4") + if err != nil { + t.Error(err) + } + if future.Greeks == nil { + t.Fatal("no greeks returned for futures contract") + } } func TestGetFundingRates(t *testing.T) { @@ -733,21 +740,21 @@ func TestGetFills(t *testing.T) { t.Skip() } // optional params - _, err := f.GetFills(context.Background(), spotPair, "", time.Time{}, time.Time{}) + _, err := f.GetFills(context.Background(), spotPair, asset.Spot, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) } - _, err = f.GetFills(context.Background(), spotPair, "", time.Time{}, time.Time{}) + _, err = f.GetFills(context.Background(), spotPair, asset.Spot, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) } _, err = f.GetFills(context.Background(), - spotPair, "", time.Unix(authStartTime, 0), time.Unix(authEndTime, 0)) + spotPair, asset.Spot, "", time.Unix(authStartTime, 0), time.Unix(authEndTime, 0)) if err != nil { t.Error(err) } _, err = f.GetFills(context.Background(), - spotPair, "", time.Unix(authEndTime, 0), time.Unix(authStartTime, 0)) + spotPair, asset.Spot, "", time.Unix(authEndTime, 0), time.Unix(authStartTime, 0)) if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } @@ -1941,7 +1948,7 @@ func TestGetFuturesPositions(t *testing.T) { func TestLoadCollateralWeightings(t *testing.T) { t.Parallel() ff := FTX{} - err := ff.LoadCollateralWeightings() + err := ff.LoadCollateralWeightings(context.Background()) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -1954,7 +1961,7 @@ func TestLoadCollateralWeightings(t *testing.T) { if !areTestAPIKeysSet() { return } - err = f.LoadCollateralWeightings() + err = f.LoadCollateralWeightings(context.Background()) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index b7884fa5315..27f47bdfda5 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -120,38 +120,38 @@ type OHLCVData struct { // FuturesData stores data for futures type FuturesData struct { - Ask float64 `json:"ask"` - Bid float64 `json:"bid"` - Change1h float64 `json:"change1h"` - Change24h float64 `json:"change24h"` - ChangeBod float64 `json:"changeBod"` - VolumeUSD24h float64 `json:"volumeUsd24h"` - Volume float64 `json:"volume"` - Description string `json:"description"` - Enabled bool `json:"enabled"` - Expired bool `json:"expired"` - Expiry time.Time `json:"expiry"` - ExpiryDescription string `json:"expiryDescription"` - Group string `json:"group"` - Index float64 `json:"index"` - IMFFactor float64 `json:"imfFactor"` - Last float64 `json:"last"` - LowerBound float64 `json:"lowerBound"` - MarginPrice float64 `json:"marginPrice"` - Mark float64 `json:"mark"` - MoveStart interface{} `json:"moveStart"` - Name string `json:"name"` - OpenInterest float64 `json:"openInterest"` - OpenInterestUSD float64 `json:"openInterestUsd"` - Perpetual bool `json:"perpetual"` - PositionLimitWeight float64 `json:"positionLimitWeight"` - PostOnly bool `json:"postOnly"` - PriceIncrement float64 `json:"priceIncrement"` - SizeIncrement float64 `json:"sizeIncrement"` - Underlying string `json:"underlying"` - UnderlyingDescription string `json:"underlyingDescription"` - UpperBound float64 `json:"upperBound"` - FutureType string `json:"type"` + Ask float64 `json:"ask"` + Bid float64 `json:"bid"` + Change1h float64 `json:"change1h"` + Change24h float64 `json:"change24h"` + ChangeBod float64 `json:"changeBod"` + VolumeUSD24h float64 `json:"volumeUsd24h"` + Volume float64 `json:"volume"` + Description string `json:"description"` + Enabled bool `json:"enabled"` + Expired bool `json:"expired"` + Expiry time.Time `json:"expiry"` + ExpiryDescription string `json:"expiryDescription"` + Group string `json:"group"` + Index float64 `json:"index"` + InitialMarginFractionFactor float64 `json:"imfFactor"` + Last float64 `json:"last"` + LowerBound float64 `json:"lowerBound"` + MarginPrice float64 `json:"marginPrice"` + Mark float64 `json:"mark"` + MoveStart interface{} `json:"moveStart"` + Name string `json:"name"` + OpenInterest float64 `json:"openInterest"` + OpenInterestUSD float64 `json:"openInterestUsd"` + Perpetual bool `json:"perpetual"` + PositionLimitWeight float64 `json:"positionLimitWeight"` + PostOnly bool `json:"postOnly"` + PriceIncrement float64 `json:"priceIncrement"` + SizeIncrement float64 `json:"sizeIncrement"` + Underlying string `json:"underlying"` + UnderlyingDescription string `json:"underlyingDescription"` + UpperBound float64 `json:"upperBound"` + FutureType string `json:"type"` } // FutureStatsData stores data on futures stats @@ -163,6 +163,11 @@ type FutureStatsData struct { PredictedExpirationPrice float64 `json:"predictedExpirationPrice"` OpenInterest float64 `json:"openInterest"` StrikePrice float64 `json:"strikePrice"` + Greeks *struct { + ImpliedVolatility float64 `json:"impliedVolatility"` + Delta float64 `json:"delta"` + Gamma float64 `json:"gamma"` + } `json:"greeks"` } // FundingRatesData stores data on funding rates @@ -768,21 +773,21 @@ type WsMarketsDataStorage struct { // WsMarketsFutureData stores websocket markets' future data type WsMarketsFutureData struct { - Name string `json:"name,omitempty"` - Underlying string `json:"underlying,omitempty"` - Description string `json:"description,omitempty"` - MarketType string `json:"type,omitempty"` - Expiry time.Time `json:"expiry,omitempty"` - Perpetual bool `json:"perpetual,omitempty"` - Expired bool `json:"expired,omitempty"` - Enabled bool `json:"enabled,omitempty"` - PostOnly bool `json:"postOnly,omitempty"` - IMFFactor float64 `json:"imfFactor,omitempty"` - UnderlyingDescription string `json:"underlyingDescription,omitempty"` - ExpiryDescription string `json:"expiryDescription,omitempty"` - MoveStart string `json:"moveStart,omitempty"` - PositionLimitWeight float64 `json:"positionLimitWeight,omitempty"` - Group string `json:"group,omitempty"` + Name string `json:"name,omitempty"` + Underlying string `json:"underlying,omitempty"` + Description string `json:"description,omitempty"` + MarketType string `json:"type,omitempty"` + Expiry time.Time `json:"expiry,omitempty"` + Perpetual bool `json:"perpetual,omitempty"` + Expired bool `json:"expired,omitempty"` + Enabled bool `json:"enabled,omitempty"` + PostOnly bool `json:"postOnly,omitempty"` + InitialMarginFractionFactor float64 `json:"imfFactor,omitempty"` + UnderlyingDescription string `json:"underlyingDescription,omitempty"` + ExpiryDescription string `json:"expiryDescription,omitempty"` + MoveStart string `json:"moveStart,omitempty"` + PositionLimitWeight float64 `json:"positionLimitWeight,omitempty"` + Group string `json:"group,omitempty"` } // WSMarkets stores websocket markets data @@ -887,7 +892,7 @@ type CollateralWeightHolder map[string]CollateralWeight // CollateralWeight holds collateral information provided by FTX // it is used to scale collateral when the currency is not in USD type CollateralWeight struct { - Initial float64 - Total float64 - IMFFactor float64 + Initial float64 + Total float64 + InitialMarginFractionFactor float64 } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 6eb04be16f6..e380dbf4446 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -203,7 +203,7 @@ func (f *FTX) Setup(exch *config.Exchange) error { } if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { - err = f.LoadCollateralWeightings() + err = f.LoadCollateralWeightings(context.TODO()) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to store collateral weightings. Err: %s", @@ -1268,26 +1268,31 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur } // CalculatePNL determines the PNL of a given position based on the PNLCalculatorRequest -func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { +func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { if pnl == nil { return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } - var result order.PNLResult - result.Time = pnl.Time + result := &order.PNLResult{ + Time: pnl.Time, + } + var err error if pnl.CalculateOffline { // PNLCalculator matches FTX's pnl calculation method calc := order.PNLCalculator{} - return calc.CalculatePNL(pnl) + result, err = calc.CalculatePNL(ctx, pnl) + if err != nil { + return nil, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, err) + } } ep := pnl.EntryPrice.InexactFloat64() - info, err := f.GetAccountInfo(context.Background()) + info, err := f.GetAccountInfo(ctx) if err != nil { return nil, err } if info.Liquidating || info.Collateral == 0 { result.IsLiquidated = true - return &result, order.ErrPositionLiquidated + return result, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, order.ErrPositionLiquidated) } for i := range info.Positions { var pair currency.Pair @@ -1304,18 +1309,22 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) result.Price = decimal.NewFromFloat(info.Positions[i].Cost) - return &result, nil + return result, nil } // order no longer active, use offline calculation - pnl.CalculateOffline = true - return f.CalculatePNL(pnl) + calc := order.PNLCalculator{} + result, err = calc.CalculatePNL(ctx, pnl) + if err != nil { + return nil, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, err) + } + return result, nil } // ScaleCollateral takes your totals and scales them according to FTX's rules func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal if calc.CalculateOffline { - if calc.CollateralCurrency == currency.USD { + if calc.CollateralCurrency.Match(currency.USD) { return calc.CollateralAmount, nil } collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] @@ -1323,8 +1332,8 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) } if calc.CollateralAmount.IsPositive() { - if collateralWeight.IMFFactor == 0 { - return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralIMFMissing) + if collateralWeight.InitialMarginFractionFactor == 0 { + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralInitialMarginFractionMissing) } var scaling decimal.Decimal if calc.IsLiquidating { @@ -1332,7 +1341,7 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula } else { scaling = decimal.NewFromFloat(collateralWeight.Initial) } - weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.IMFFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) + weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.InitialMarginFractionFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) } else { result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) @@ -1348,11 +1357,11 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula return decimal.Zero, err } for i := range wallet { - if currency.NewCode(wallet[i].ID) != calc.CollateralCurrency { + if !currency.NewCode(wallet[i].ID).Match(calc.CollateralCurrency) { continue } for j := range balances { - if currency.NewCode(balances[j].Coin) != calc.CollateralCurrency { + if !currency.NewCode(balances[j].Coin).Match(calc.CollateralCurrency) { continue } scaled := wallet[i].CollateralWeight * balances[j].USDValue @@ -1380,7 +1389,7 @@ func (f *FTX) CalculateTotalCollateral(ctx context.Context, collateralAssets []o Currency: collateralAssets[i].CollateralCurrency, Amount: collateral, } - if collateralAssets[i].CollateralCurrency != currency.USD { + if !collateralAssets[i].CollateralCurrency.Match(currency.USD) { curr.ValueCurrency = currency.USD } @@ -1394,7 +1403,7 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency if !a.IsFutures() { return nil, fmt.Errorf("%w futures asset type only", common.ErrFunctionNotSupported) } - fills, err := f.GetFills(ctx, cp, "200", start, end) + fills, err := f.GetFills(ctx, cp, a, "200", start, end) if err != nil { return nil, err } @@ -1412,7 +1421,7 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency resp = append(resp, order.Detail{ Side: side, Pair: cp, - ID: fmt.Sprintf("%v", fills[i].ID), + ID: strconv.FormatInt(fills[i].ID, 10), Price: price, Amount: fills[i].Size, AssetType: a, diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index 688a23dd3f5..95fb3e4c1dc 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -423,7 +423,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, bankTransactionType exch var fee float64 if (bankTransactionType == exchange.Swift || bankTransactionType == exchange.WireTransfer) && - c == currency.USD { + c.Match(currency.USD) { fee = 40 } else if (bankTransactionType == exchange.SEPA || bankTransactionType == exchange.WireTransfer) && diff --git a/exchanges/order/README.md b/exchanges/order/README.md index 4967af9ba5c..0128ff4baa8 100644 --- a/exchanges/order/README.md +++ b/exchanges/order/README.md @@ -1,16 +1,16 @@ -# GoCryptoTrader package Orders +# GoCryptoTrader package Order - + [![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) [![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) -[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/orders) +[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/order) [![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) -This orders package is part of the GoCryptoTrader codebase. +This order package is part of the GoCryptoTrader codebase. ## This is still in active development @@ -18,12 +18,14 @@ You can track ideas, planned features and what's in progress on this Trello boar Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) -## Current Features for orders +## Current Features for order + This package services the exchanges package with order handling. - - Creation of order - - Deletion of order - - Order tracking + - Creation of order + - Deletion of order + - Order tracking + ++ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders, keeping a history of orders and tracking unrealised & realised PNL ### Please click GoDocs chevron above to view current GoDoc information for this package @@ -45,4 +47,3 @@ When submitting a PR, please abide by our coding guidelines: If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to: ***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc*** - diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b3b05121608..8080ef3a80c 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -1,6 +1,7 @@ package order import ( + "context" "errors" "fmt" "strings" @@ -442,7 +443,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { baseFee := cal.Fee.Div(amount) cal.Fee = baseFee.Mul(first) cal.Amount = first - result, err = p.PNLCalculation.CalculatePNL(cal) + result, err = p.PNLCalculation.CalculatePNL(context.TODO(), cal) if err != nil { return err } @@ -466,9 +467,9 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { cal.EntryPrice = price cal.Time = cal.Time.Add(1) cal.PNLHistory = p.pnlHistory - result, err = p.PNLCalculation.CalculatePNL(cal) + result, err = p.PNLCalculation.CalculatePNL(context.TODO(), cal) } else { - result, err = p.PNLCalculation.CalculatePNL(cal) + result, err = p.PNLCalculation.CalculatePNL(context.TODO(), cal) } if err != nil { if !errors.Is(err, ErrPositionLiquidated) { @@ -517,7 +518,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { // CalculatePNL this is a localised generic way of calculating open // positions' worth, it is an implementation of the PNLCalculation interface -func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, error) { +func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorRequest) (*PNLResult, error) { if calc == nil { return nil, ErrNilPNLCalculator } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 69553a0da8a..753be2a3b91 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -1,6 +1,7 @@ package order import ( + "context" "errors" "testing" "time" @@ -20,7 +21,7 @@ type FakePNL struct { } // CalculatePNL overrides default pnl calculations -func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { +func (f *FakePNL) CalculatePNL(context.Context, *PNLCalculatorRequest) (*PNLResult, error) { if f.err != nil { return nil, f.err } @@ -688,19 +689,20 @@ func TestSetupPositionTracker(t *testing.T) { func TestCalculatePNL(t *testing.T) { t.Parallel() p := &PNLCalculator{} - _, err := p.CalculatePNL(nil) + _, err := p.CalculatePNL(context.Background(), nil) if !errors.Is(err, ErrNilPNLCalculator) { t.Errorf("received '%v' expected '%v", err, ErrNilPNLCalculator) } - _, err = p.CalculatePNL(&PNLCalculatorRequest{}) + _, err = p.CalculatePNL(context.Background(), &PNLCalculatorRequest{}) if !errors.Is(err, errCannotCalculateUnrealisedPNL) { t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) } - _, err = p.CalculatePNL(&PNLCalculatorRequest{ - OrderDirection: Short, - CurrentDirection: Long, - }) + _, err = p.CalculatePNL(context.Background(), + &PNLCalculatorRequest{ + OrderDirection: Short, + CurrentDirection: Long, + }) if !errors.Is(err, errCannotCalculateUnrealisedPNL) { t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 185c8f5895a..127fa31cc86 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -45,7 +45,7 @@ var ( // PNLCalculation is an interface to allow multiple // ways of calculating PNL to be used for futures positions type PNLCalculation interface { - CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) + CalculatePNL(context.Context, *PNLCalculatorRequest) (*PNLResult, error) } // CollateralManagement is an interface that allows diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go index d32f2dcefad..56f73a1bb46 100644 --- a/exchanges/yobit/yobit.go +++ b/exchanges/yobit/yobit.go @@ -372,7 +372,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, amount float64, bankTran switch bankTransactionType { case exchange.PerfectMoney: - if c == currency.USD { + if c.Match(currency.USD) { fee = 0.02 * amount } case exchange.Payeer: @@ -394,7 +394,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, amount float64, bankTran fee = 0.04 * amount } case exchange.Capitalist: - if c == currency.USD { + if c.Match(currency.USD) { fee = 0.06 * amount } } @@ -407,7 +407,7 @@ func getInternationalBankDepositFee(c currency.Code, bankTransactionType exchang var fee float64 switch bankTransactionType { case exchange.PerfectMoney: - if c == currency.USD { + if c.Match(currency.USD) { fee = 0 } case exchange.Payeer: From ac02d533b41302e25b1f5a62f911883f1326ad66 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 11 Jan 2022 16:05:23 +1100 Subject: [PATCH 008/171] Addresses Shazniterinos, var names, expanded tests --- .../exchanges_templates/orders.tmpl | 2 +- engine/order_manager.go | 6 +- engine/order_manager_test.go | 54 +++++++++++- engine/rpcserver.go | 85 +++++++++---------- exchanges/ftx/ftx.go | 18 ++-- exchanges/ftx/ftx_test.go | 60 +++++++++++-- exchanges/ftx/ftx_wrapper.go | 11 +-- exchanges/order/README.md | 2 +- exchanges/order/futures.go | 49 ++++++----- exchanges/order/futures_test.go | 22 ++--- exchanges/order/futures_types.go | 4 +- 11 files changed, 204 insertions(+), 109 deletions(-) diff --git a/cmd/documentation/exchanges_templates/orders.tmpl b/cmd/documentation/exchanges_templates/orders.tmpl index a960bbdbd8c..cbfe7c57900 100644 --- a/cmd/documentation/exchanges_templates/orders.tmpl +++ b/cmd/documentation/exchanges_templates/orders.tmpl @@ -7,7 +7,7 @@ - Deletion of order - Order tracking -+ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders, keeping a history of orders and tracking unrealised & realised PNL ++ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders that GoCryptoTrader processes. It keeps a running history of realised and unreaslied PNL to allow a trader to track their profits. Positions are closed once the exposure reaches zero, then upon a new futures order being processed, a new position is created. To view futures positions, see the GRPC command `getfuturesposition` ### Please click GoDocs chevron above to view current GoDoc information for this package {{template "contributions"}} diff --git a/engine/order_manager.go b/engine/order_manager.go index b64ee558116..a89dae19513 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -227,7 +227,7 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { // GetFuturesPositionsForExchange returns futures positions stored within // the order manager's futures position tracker that match the provided params -func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*order.PositionTracker, error) { +func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]order.PositionStats, error) { if m == nil { return nil, fmt.Errorf("order manager %w", ErrNilSubsystem) } @@ -238,7 +238,7 @@ func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.It return nil, errFuturesTrackerNotSetup } if !item.IsFutures() { - return nil, fmt.Errorf("%v %w", item, order.ErrNotFutureAsset) + return nil, fmt.Errorf("%v %w", item, order.ErrNotFuturesAsset) } return m.orderStore.futuresPositionController.GetPositionsForExchange(exch, item, pair) @@ -257,7 +257,7 @@ func (m *OrderManager) ClearFuturesTracking(exch string, item asset.Item, pair c return errFuturesTrackerNotSetup } if !item.IsFutures() { - return fmt.Errorf("%v %w", item, order.ErrNotFutureAsset) + return fmt.Errorf("%v %w", item, order.ErrNotFuturesAsset) } return m.orderStore.futuresPositionController.ClearPositionsForExchange(exch, item, pair) diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 78b9581a40d..7353755086b 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -1169,14 +1169,35 @@ func TestGetFuturesPositionsForExchange(t *testing.T) { } o.orderStore.futuresPositionController = order.SetupPositionController() _, err = o.GetFuturesPositionsForExchange("test", asset.Spot, cp) - if !errors.Is(err, order.ErrNotFutureAsset) { - t.Errorf("received '%v', expected '%v'", err, order.ErrNotFutureAsset) + if !errors.Is(err, order.ErrNotFuturesAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFuturesAsset) } _, err = o.GetFuturesPositionsForExchange("test", asset.Futures, cp) if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) } + + err = o.orderStore.futuresPositionController.TrackNewOrder(&order.Detail{ + ID: "test", + Date: time.Now(), + Exchange: "test", + AssetType: asset.Futures, + Pair: cp, + Side: order.Buy, + Amount: 1, + Price: 1}) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + resp, err := o.GetFuturesPositionsForExchange("test", asset.Futures, cp) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + if len(resp) != 1 { + t.Error("expected 1 position") + } + o = nil _, err = o.GetFuturesPositionsForExchange("test", asset.Futures, cp) if !errors.Is(err, ErrNilSubsystem) { @@ -1199,14 +1220,39 @@ func TestClearFuturesPositionsForExchange(t *testing.T) { } o.orderStore.futuresPositionController = order.SetupPositionController() err = o.ClearFuturesTracking("test", asset.Spot, cp) - if !errors.Is(err, order.ErrNotFutureAsset) { - t.Errorf("received '%v', expected '%v'", err, order.ErrNotFutureAsset) + if !errors.Is(err, order.ErrNotFuturesAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFuturesAsset) } err = o.ClearFuturesTracking("test", asset.Futures, cp) if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) } + + err = o.orderStore.futuresPositionController.TrackNewOrder(&order.Detail{ + ID: "test", + Date: time.Now(), + Exchange: "test", + AssetType: asset.Futures, + Pair: cp, + Side: order.Buy, + Amount: 1, + Price: 1}) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + err = o.ClearFuturesTracking("test", asset.Futures, cp) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + resp, err := o.GetFuturesPositionsForExchange("test", asset.Futures, cp) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + if len(resp) != 0 { + t.Errorf("expected no position, received '%v'", len(resp)) + } + o = nil err = o.ClearFuturesTracking("test", asset.Futures, cp) if !errors.Is(err, ErrNilSubsystem) { diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 6f917145fb7..d28136d9903 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4174,65 +4174,64 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if r.PositionLimit > 0 && len(response.Positions) >= int(r.PositionLimit) { break } - stats := pos[i].GetStats() - response.TotalOrders += int64(len(stats.Orders)) + response.TotalOrders += int64(len(pos[i].Orders)) details := &gctrpc.FuturePosition{ - Status: stats.Status.String(), - UnrealisedPNL: stats.UnrealisedPNL.String(), - RealisedPNL: stats.RealisedPNL.String(), + Status: pos[i].Status.String(), + UnrealisedPNL: pos[i].UnrealisedPNL.String(), + RealisedPNL: pos[i].RealisedPNL.String(), } - if !stats.UnrealisedPNL.IsZero() { - details.UnrealisedPNL = stats.UnrealisedPNL.String() + if !pos[i].UnrealisedPNL.IsZero() { + details.UnrealisedPNL = pos[i].UnrealisedPNL.String() } - if !stats.RealisedPNL.IsZero() { - details.RealisedPNL = stats.RealisedPNL.String() + if !pos[i].RealisedPNL.IsZero() { + details.RealisedPNL = pos[i].RealisedPNL.String() } - if stats.LatestDirection != order.UnknownSide { - details.CurrentDirection = stats.LatestDirection.String() + if pos[i].LatestDirection != order.UnknownSide { + details.CurrentDirection = pos[i].LatestDirection.String() } - if len(stats.PNLHistory) > 0 { - details.OpeningDate = stats.PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) - if stats.Status == order.Closed { - details.ClosingDate = stats.PNLHistory[len(stats.PNLHistory)-1].Time.Format(common.SimpleTimeFormatWithTimezone) + if len(pos[i].PNLHistory) > 0 { + details.OpeningDate = pos[i].PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) + if pos[i].Status == order.Closed { + details.ClosingDate = pos[i].PNLHistory[len(pos[i].PNLHistory)-1].Time.Format(common.SimpleTimeFormatWithTimezone) } } - totalRealisedPNL = totalRealisedPNL.Add(stats.RealisedPNL) - totalUnrealisedPNL = totalUnrealisedPNL.Add(stats.UnrealisedPNL) + totalRealisedPNL = totalRealisedPNL.Add(pos[i].RealisedPNL) + totalUnrealisedPNL = totalUnrealisedPNL.Add(pos[i].UnrealisedPNL) if !r.Verbose { response.Positions = append(response.Positions, details) continue } - for j := range stats.Orders { + for j := range pos[i].Orders { var trades []*gctrpc.TradeHistory - for k := range stats.Orders[j].Trades { + for k := range pos[i].Orders[j].Trades { trades = append(trades, &gctrpc.TradeHistory{ - CreationTime: stats.Orders[j].Trades[k].Timestamp.Unix(), - Id: stats.Orders[j].Trades[k].TID, - Price: stats.Orders[j].Trades[k].Price, - Amount: stats.Orders[j].Trades[k].Amount, - Exchange: stats.Orders[j].Trades[k].Exchange, - AssetType: stats.Asset.String(), - OrderSide: stats.Orders[j].Trades[k].Side.String(), - Fee: stats.Orders[j].Trades[k].Fee, - Total: stats.Orders[j].Trades[k].Total, + CreationTime: pos[i].Orders[j].Trades[k].Timestamp.Unix(), + Id: pos[i].Orders[j].Trades[k].TID, + Price: pos[i].Orders[j].Trades[k].Price, + Amount: pos[i].Orders[j].Trades[k].Amount, + Exchange: pos[i].Orders[j].Trades[k].Exchange, + AssetType: pos[i].Asset.String(), + OrderSide: pos[i].Orders[j].Trades[k].Side.String(), + Fee: pos[i].Orders[j].Trades[k].Fee, + Total: pos[i].Orders[j].Trades[k].Total, }) } details.Orders = append(details.Orders, &gctrpc.OrderDetails{ - Exchange: stats.Orders[j].Exchange, - Id: stats.Orders[j].ID, - ClientOrderId: stats.Orders[j].ClientOrderID, - BaseCurrency: stats.Orders[j].Pair.Base.String(), - QuoteCurrency: stats.Orders[j].Pair.Quote.String(), - AssetType: stats.Orders[j].AssetType.String(), - OrderSide: stats.Orders[j].Side.String(), - OrderType: stats.Orders[j].Type.String(), - CreationTime: stats.Orders[j].Date.Unix(), - UpdateTime: stats.Orders[j].LastUpdated.Unix(), - Status: stats.Orders[j].Status.String(), - Price: stats.Orders[j].Price, - Amount: stats.Orders[j].Amount, - Fee: stats.Orders[j].Fee, - Cost: stats.Orders[j].Cost, + Exchange: pos[i].Orders[j].Exchange, + Id: pos[i].Orders[j].ID, + ClientOrderId: pos[i].Orders[j].ClientOrderID, + BaseCurrency: pos[i].Orders[j].Pair.Base.String(), + QuoteCurrency: pos[i].Orders[j].Pair.Quote.String(), + AssetType: pos[i].Orders[j].AssetType.String(), + OrderSide: pos[i].Orders[j].Side.String(), + OrderType: pos[i].Orders[j].Type.String(), + CreationTime: pos[i].Orders[j].Date.Unix(), + UpdateTime: pos[i].Orders[j].LastUpdated.Unix(), + Status: pos[i].Orders[j].Status.String(), + Price: pos[i].Orders[j].Price, + Amount: pos[i].Orders[j].Amount, + Fee: pos[i].Orders[j].Fee, + Cost: pos[i].Orders[j].Cost, Trades: trades, }) } diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index f76fdbfae1d..205bce4754f 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1635,27 +1635,19 @@ func (f *FTX) LoadCollateralWeightings(ctx context.Context) error { return nil } -func (c CollateralWeightHolder) isLoaded() bool { +func (c CollateralWeightHolder) hasData() bool { return len(c) > 0 } func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { - currencyCollateral, ok := c[code] - if !ok { - currencyCollateral = CollateralWeight{Total: weighting} - } else { - currencyCollateral.Total = weighting - } + currencyCollateral := c[code] + currencyCollateral.Total = weighting c[code] = currencyCollateral } func (c CollateralWeightHolder) loadInitialMarginFraction(code string, imf float64) { - currencyCollateral, ok := c[code] - if !ok { - currencyCollateral = CollateralWeight{InitialMarginFractionFactor: imf} - } else { - currencyCollateral.InitialMarginFractionFactor = imf - } + currencyCollateral := c[code] + currencyCollateral.InitialMarginFractionFactor = imf c[code] = currencyCollateral } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index a76db906095..4808850a0b0 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1922,11 +1922,8 @@ func TestCalculatePNL(t *testing.T) { } } results := p.GetPositions() - for i := range results { - _, err = results[i].GetLatestPNLSnapshot() - if err != nil { - t.Error(err) - } + if len(orders) > 0 && len(results) == 0 { + t.Error("expected position(s) to be generated") } } @@ -1955,7 +1952,7 @@ func TestLoadCollateralWeightings(t *testing.T) { if len(ff.collateralWeight) == 0 { t.Fatal("expected some weight") } - if !ff.collateralWeight.isLoaded() { + if !ff.collateralWeight.hasData() { t.Error("expected loaded weight") } if !areTestAPIKeysSet() { @@ -1969,3 +1966,54 @@ func TestLoadCollateralWeightings(t *testing.T) { t.Fatal("expected some weight") } } + +func TestLoadTotalIMF(t *testing.T) { + t.Parallel() + c := CollateralWeightHolder{} + c.loadTotal("cw", 1) + if _, ok := c["cw"]; !ok { + t.Error("expected entry") + } + c.loadInitialMarginFraction("cw", 1) + cw, ok := c["cw"] + if !ok { + t.Error("expected entry") + } + if cw.Total != 1 { + t.Errorf("expected '1', received '%v'", cw.Total) + } + if cw.InitialMarginFractionFactor != 1 { + t.Errorf("expected '1', received '%v'", cw.InitialMarginFractionFactor) + } +} + +func TestLoadCollateralWeight(t *testing.T) { + t.Parallel() + c := CollateralWeightHolder{} + c.load("test", 1, 2, 3) + cw, ok := c["test"] + if !ok { + t.Fatal("expected loaded collateral weight") + } + if cw.Initial != 1 { + t.Errorf("expected '1', received '%v'", cw.Total) + } + if cw.Total != 2 { + t.Errorf("expected '2', received '%v'", cw.InitialMarginFractionFactor) + } + if cw.InitialMarginFractionFactor != 3 { + t.Errorf("expected '3', received '%v'", cw.Total) + } +} + +func TestCollateralWeightHasData(t *testing.T) { + t.Parallel() + c := CollateralWeightHolder{} + if c.hasData() { + t.Error("expected false") + } + c.load("test", 1, 2, 3) + if !c.hasData() { + t.Error("expected true") + } +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index e380dbf4446..7b36f0d6f5c 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1325,15 +1325,16 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula var result decimal.Decimal if calc.CalculateOffline { if calc.CollateralCurrency.Match(currency.USD) { + // FTX bases scales all collateral into USD amounts return calc.CollateralAmount, nil } collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] if !ok { - return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) + return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) } if calc.CollateralAmount.IsPositive() { if collateralWeight.InitialMarginFractionFactor == 0 { - return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralInitialMarginFractionMissing) + return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralInitialMarginFractionMissing) } var scaling decimal.Decimal if calc.IsLiquidating { @@ -1350,11 +1351,11 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula } wallet, err := f.GetCoins(ctx) if err != nil { - return decimal.Zero, err + return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, err) } balances, err := f.GetBalances(ctx) if err != nil { - return decimal.Zero, err + return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, err) } for i := range wallet { if !currency.NewCode(wallet[i].ID).Match(calc.CollateralCurrency) { @@ -1369,7 +1370,7 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula return result, nil } } - return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) + return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) } // CalculateTotalCollateral scales collateral and determines how much collateral you can use for positions diff --git a/exchanges/order/README.md b/exchanges/order/README.md index 0128ff4baa8..f3ae045fd9b 100644 --- a/exchanges/order/README.md +++ b/exchanges/order/README.md @@ -25,7 +25,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader - Deletion of order - Order tracking -+ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders, keeping a history of orders and tracking unrealised & realised PNL ++ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders that GoCryptoTrader processes. It keeps a running history of realised and unreaslied PNL to allow a trader to track their profits. Positions are closed once the exposure reaches zero, then upon a new futures order being processed, a new position is created. To view futures positions, see the GRPC command `getfuturesposition` ### Please click GoDocs chevron above to view current GoDoc information for this package diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 8080ef3a80c..4936325a22d 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "sort" "strings" "time" @@ -29,23 +30,27 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return errNilOrder } if !d.AssetType.IsFutures() { - return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, ErrNotFutureAsset) + return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, ErrNotFuturesAsset) } if c == nil { return common.ErrNilPointer } c.m.Lock() defer c.m.Unlock() - if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)]; !ok { - c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + exchM, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)] + if !ok { + exchM = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[strings.ToLower(d.Exchange)] = exchM } - if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType]; !ok { - c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType] = make(map[currency.Pair]*MultiPositionTracker) + itemM, ok := exchM[d.AssetType] + if !ok { + itemM = make(map[currency.Pair]*MultiPositionTracker) + exchM[d.AssetType] = itemM } var err error - mpt, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] + multiPositionTracker, ok := itemM[d.Pair] if !ok { - mpt, err = SetupMultiPositionTracker(&MultiPositionTrackerSetup{ + multiPositionTracker, err = SetupMultiPositionTracker(&MultiPositionTrackerSetup{ Exchange: strings.ToLower(d.Exchange), Asset: d.AssetType, Pair: d.Pair, @@ -54,21 +59,21 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { if err != nil { return err } - c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] = mpt + itemM[d.Pair] = multiPositionTracker } - return mpt.TrackNewOrder(d) + return multiPositionTracker.TrackNewOrder(d) } // GetPositionsForExchange returns all positions for an // exchange, asset pair that is stored in the position controller -func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*PositionTracker, error) { +func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]PositionStats, error) { if c == nil { return nil, common.ErrNilPointer } c.m.Lock() defer c.m.Unlock() if !item.IsFutures() { - return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFutureAsset) + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) } exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] if !ok { @@ -95,7 +100,7 @@ func (c *PositionController) ClearPositionsForExchange(exch string, item asset.I c.m.Lock() defer c.m.Unlock() if !item.IsFutures() { - return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFutureAsset) + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) } exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] if !ok { @@ -121,7 +126,7 @@ func (c *PositionController) ClearPositionsForExchange(exch string, item asset.I if err != nil { return err } - c.positionTrackerControllers[strings.ToLower(exch)][item][pair] = newMPT + itemM[pair] = newMPT return nil } @@ -134,7 +139,7 @@ func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPosition return nil, errExchangeNameEmpty } if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { - return nil, ErrNotFutureAsset + return nil, ErrNotFuturesAsset } if setup.Pair.IsEmpty() { return nil, ErrPairIsEmpty @@ -158,13 +163,17 @@ func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPosition } // GetPositions returns all positions -func (e *MultiPositionTracker) GetPositions() []*PositionTracker { +func (e *MultiPositionTracker) GetPositions() []PositionStats { if e == nil { return nil } e.m.Lock() defer e.m.Unlock() - return e.positions + var resp []PositionStats + for i := range e.positions { + resp = append(resp, e.positions[i].GetStats()) + } + return resp } // TrackNewOrder upserts an order to the tracker and updates position @@ -235,7 +244,7 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) return nil, errNilSetup } if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { - return nil, ErrNotFutureAsset + return nil, ErrNotFuturesAsset } if setup.Pair.IsEmpty() { return nil, ErrPairIsEmpty @@ -584,9 +593,6 @@ func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) totalFees = totalFees.Add(pnlHistory[i].Fee) } - if realisedPNL.IsZero() { - return decimal.Zero - } return realisedPNL.Sub(totalFees) } @@ -603,5 +609,8 @@ func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, erro } } pnlHistory = append(pnlHistory, *entry) + sort.Slice(pnlHistory, func(i, j int) bool { + return pnlHistory[i].Time.Before(pnlHistory[j].Time) + }) return pnlHistory, nil } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 753be2a3b91..d79d188773a 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -14,7 +14,7 @@ import ( const testExchange = "test" -// FakePNL is implements PNL interface +// FakePNL implements PNL interface type FakePNL struct { err error result *PNLResult @@ -202,7 +202,7 @@ func TestSetupMultiPositionTracker(t *testing.T) { } setup.Exchange = testExchange _, err = SetupMultiPositionTracker(setup) - if !errors.Is(err, ErrNotFutureAsset) { + if !errors.Is(err, ErrNotFuturesAsset) { t.Error(err) } setup.Asset = asset.Futures @@ -365,7 +365,7 @@ func TestPositionControllerTestTrackNewOrder(t *testing.T) { Side: Long, ID: "lol", }) - if !errors.Is(err, ErrNotFutureAsset) { + if !errors.Is(err, ErrNotFuturesAsset) { t.Error(err) } @@ -452,7 +452,7 @@ func TestGetPositions(t *testing.T) { if len(positions) != 1 { t.Fatal("expected 1") } - if positions[0].exchange != testExchange { + if positions[0].Exchange != testExchange { t.Error("expected 'test'") } @@ -487,8 +487,8 @@ func TestGetPositionsForExchange(t *testing.T) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) } _, err = c.GetPositionsForExchange(testExchange, asset.Spot, p) - if !errors.Is(err, ErrNotFutureAsset) { - t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + if !errors.Is(err, ErrNotFuturesAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) } c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) @@ -518,7 +518,7 @@ func TestGetPositionsForExchange(t *testing.T) { if len(pos) != 1 { t.Fatal("expected 1") } - if pos[0].exchange != testExchange { + if pos[0].Exchange != testExchange { t.Error("expected test") } c = nil @@ -549,8 +549,8 @@ func TestClearPositionsForExchange(t *testing.T) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) } err = c.ClearPositionsForExchange(testExchange, asset.Spot, p) - if !errors.Is(err, ErrNotFutureAsset) { - t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + if !errors.Is(err, ErrNotFuturesAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) } c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) @@ -632,8 +632,8 @@ func TestSetupPositionTracker(t *testing.T) { p, err = m.SetupPositionTracker(&PositionTrackerSetup{ Asset: asset.Spot, }) - if !errors.Is(err, ErrNotFutureAsset) { - t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + if !errors.Is(err, ErrNotFuturesAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) } if p != nil { t.Error("expected nil") diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 127fa31cc86..0f64ad5629c 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -26,8 +26,8 @@ var ( // ErrPositionLiquidated is raised when checking PNL status only for // it to be liquidated ErrPositionLiquidated = errors.New("position liquidated") - // ErrNotFutureAsset returned when futures data is requested on a non-futures asset - ErrNotFutureAsset = errors.New("asset type is not futures") + // ErrNotFuturesAsset returned when futures data is requested on a non-futures asset + ErrNotFuturesAsset = errors.New("asset type is not futures") errExchangeNameEmpty = errors.New("exchange name empty") errTimeUnset = errors.New("time unset") From bac9ce789c433a15c18b7dae4e5194161930b3bf Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 13 Jan 2022 11:57:28 +1100 Subject: [PATCH 009/171] Returns subaccount name, provides USD values when offlinecalc --- cmd/gctcli/commands.go | 2 +- engine/rpcserver.go | 42 +- engine/rpcserver_test.go | 40 +- exchanges/ftx/ftx.go | 31 + exchanges/ftx/ftx_test.go | 32 +- exchanges/ftx/ftx_types.go | 114 +-- exchanges/ftx/ftx_wrapper.go | 16 +- exchanges/order/futures_types.go | 3 + gctrpc/rpc.pb.go | 1453 +++++++++++++++--------------- gctrpc/rpc.proto | 15 +- gctrpc/rpc.swagger.json | 9 + 11 files changed, 965 insertions(+), 792 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 3f0a4749742..4a306bb3127 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4941,7 +4941,7 @@ var getCollateralCommand = &cli.Command{ &cli.BoolFlag{ Name: "calculateoffline", Aliases: []string{"c"}, - Usage: "use local scaling methods instead of requesting additional API information, depending on individual exchange support", + Usage: "use local scaling calculations instead of requesting the collateral values directly, depending on individual exchange support", }, &cli.BoolFlag{ Name: "includebreakdown", diff --git a/engine/rpcserver.go b/engine/rpcserver.go index d28136d9903..93292b47fa0 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4125,6 +4125,9 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if err != nil { return nil, err } + if !a.IsFutures() { + return nil, fmt.Errorf("%s %w", a, order.ErrNotFuturesAsset) + } var start, end time.Time if r.StartDate != "" { start, err = time.Parse(common.SimpleTimeFormat, r.StartDate) @@ -4168,7 +4171,10 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if err != nil { return nil, err } - response := &gctrpc.GetFuturesPositionsResponse{} + b := exch.GetBase() + response := &gctrpc.GetFuturesPositionsResponse{ + SubAccount: b.API.Credentials.Subaccount, + } var totalRealisedPNL, totalUnrealisedPNL decimal.Decimal for i := range pos { if r.PositionLimit > 0 && len(response.Positions) >= int(r.PositionLimit) { @@ -4264,11 +4270,13 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if err != nil { return nil, err } + if !a.IsFutures() { + return nil, fmt.Errorf("%s %w", a, order.ErrNotFuturesAsset) + } ai, err := exch.FetchAccountInfo(ctx, a) if err != nil { return nil, err } - var calculators []order.CollateralCalculator var acc account.SubAccount if r.SubAccount != "" { @@ -4282,15 +4290,25 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe acc = ai.Accounts[0] } for i := range acc.Currencies { - if acc.Currencies[i].TotalValue == 0 { - continue - } - calculators = append(calculators, order.CollateralCalculator{ + cal := order.CollateralCalculator{ CalculateOffline: r.CalculateOffline, CollateralCurrency: acc.Currencies[i].CurrencyName, Asset: a, CollateralAmount: decimal.NewFromFloat(acc.Currencies[i].TotalValue), - }) + } + if r.CalculateOffline && !acc.Currencies[i].CurrencyName.Match(currency.USD) { + var tick *ticker.Price + tick, err = exch.FetchTicker(ctx, currency.NewPair(acc.Currencies[i].CurrencyName, currency.USD), asset.Spot) + if err != nil { + log.Errorf(log.GRPCSys, fmt.Sprintf("GetCollateral offline calculation via FetchTicker %s %s", exch.GetName(), err)) + continue + } + if tick.Last == 0 { + continue + } + cal.USDPrice = decimal.NewFromFloat(tick.Last) + } + calculators = append(calculators, cal) } collateral, err := exch.CalculateTotalCollateral(ctx, calculators) @@ -4298,16 +4316,22 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe return nil, err } + b := exch.GetBase() result := &gctrpc.GetCollateralResponse{ + SubAccount: b.API.Credentials.Subaccount, TotalCollateral: collateral.TotalCollateral.String(), } if r.IncludeBreakdown { for i := range collateral.BreakdownByCurrency { - result.CurrencyBreakdown = append(result.CurrencyBreakdown, &gctrpc.CollateralForCurrency{ + cb := &gctrpc.CollateralForCurrency{ Currency: collateral.BreakdownByCurrency[i].Currency.String(), ScaledCollateral: collateral.BreakdownByCurrency[i].Amount.String(), ScaledToCurrency: collateral.BreakdownByCurrency[i].ValueCurrency.String(), - }) + } + if collateral.BreakdownByCurrency[i].Error != nil { + cb.Error = collateral.BreakdownByCurrency[i].Error.Error() + } + result.CurrencyBreakdown = append(result.CurrencyBreakdown, cb) } } return result, nil diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 858ff86af1f..6f97f87bbc4 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -2049,6 +2049,12 @@ func TestGetFuturesPositions(t *testing.T) { Available: currency.Pairs{cp}, Enabled: currency.Pairs{cp}, } + b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } fakeExchange := fExchange{ IBotExchange: exch, } @@ -2079,8 +2085,8 @@ func TestGetFuturesPositions(t *testing.T) { }, Verbose: true, }) - if err != nil { - t.Error(err) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) } if r == nil { t.Fatal("expected not nil response") @@ -2091,6 +2097,20 @@ func TestGetFuturesPositions(t *testing.T) { if r.TotalOrders != 1 { t.Fatal("expected 1 order") } + + _, err = s.GetFuturesPositions(context.Background(), &gctrpc.GetFuturesPositionsRequest{ + Exchange: fakeExchangeName, + Asset: asset.Spot.String(), + Pair: &gctrpc.CurrencyPair{ + Delimiter: currency.DashDelimiter, + Base: cp.Base.String(), + Quote: cp.Quote.String(), + }, + Verbose: true, + }) + if !errors.Is(err, order.ErrNotFuturesAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFuturesAsset) + } } func TestGetCollateral(t *testing.T) { @@ -2116,6 +2136,12 @@ func TestGetCollateral(t *testing.T) { Available: currency.Pairs{cp}, Enabled: currency.Pairs{cp}, } + b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } fakeExchange := fExchange{ IBotExchange: exch, } @@ -2155,4 +2181,14 @@ func TestGetCollateral(t *testing.T) { if r.TotalCollateral != "1337" { t.Error("expected 1337") } + + _, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + Exchange: fakeExchangeName, + Asset: asset.Spot.String(), + IncludeBreakdown: true, + SubAccount: "1337", + }) + if !errors.Is(err, order.ErrNotFuturesAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFuturesAsset) + } } diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 205bce4754f..0fa7c6ad30b 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -40,6 +40,7 @@ const ( getHistoricalData = "/markets/%s/candles" getFutures = "/futures" getFuture = "/futures/" + getExpiredFutures = "/expired_futures" getFutureStats = "/futures/%s/stats" getFundingRates = "/funding_rates" getIndexWeights = "/indexes/%s/weights" @@ -309,6 +310,36 @@ func (f *FTX) GetFutureStats(ctx context.Context, futureName string) (FutureStat return resp.Data, f.SendHTTPRequest(ctx, exchange.RestSpot, fmt.Sprintf(getFutureStats, futureName), &resp) } +// GetExpiredFuture returns information on an expired futures contract +func (f *FTX) GetExpiredFuture(ctx context.Context, pair currency.Pair) (FuturesData, error) { + resp := struct { + Data []FuturesData `json:"result"` + }{} + + p, err := f.FormatSymbol(pair, asset.Futures) + if err != nil { + return FuturesData{}, err + } + err = f.SendHTTPRequest(ctx, exchange.RestSpot, getExpiredFutures, &resp) + if err != nil { + return FuturesData{}, err + } + for i := range resp.Data { + if resp.Data[i].Name == p { + return resp.Data[i], nil + } + } + return FuturesData{}, fmt.Errorf("%s %s %w", f.Name, p, currency.ErrPairNotFound) +} + +// GetExpiredFutures returns information on expired futures contracts +func (f *FTX) GetExpiredFutures(ctx context.Context) ([]FuturesData, error) { + resp := struct { + Data []FuturesData `json:"result"` + }{} + return resp.Data, f.SendHTTPRequest(ctx, exchange.RestSpot, getExpiredFutures, &resp) +} + // GetFundingRates gets data on funding rates func (f *FTX) GetFundingRates(ctx context.Context, startTime, endTime time.Time, future string) ([]FundingRatesData, error) { resp := struct { diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 4808850a0b0..d629793450e 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1774,7 +1774,8 @@ func TestScaleCollateral(t *testing.T) { CalculateOffline: true, }) if err != nil { - if errors.Is(err, errCollateralCurrencyNotFound) { + if errors.Is(err, errCollateralCurrencyNotFound) || + errors.Is(err, order.ErrUSDValueRequired) { continue } t.Error(err) @@ -1796,6 +1797,19 @@ func TestScaleCollateral(t *testing.T) { } liquidationScaling += scaled.InexactFloat64() + _, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.Zero, + IsLiquidating: true, + CalculateOffline: true, + }) + if !errors.Is(err, order.ErrUSDValueRequired) { + t.Errorf("received '%v' exepected '%v'", err, order.ErrUSDValueRequired) + } + _, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ CollateralCurrency: currency.NewCode(coin), Asset: asset.Spot, @@ -2017,3 +2031,19 @@ func TestCollateralWeightHasData(t *testing.T) { t.Error("expected true") } } + +func TestGetExpiredFutures(t *testing.T) { + t.Parallel() + _, err := f.GetExpiredFutures(context.Background()) + if err != nil { + t.Error(err) + } +} + +func TestGetExpiredFuture(t *testing.T) { + t.Parallel() + _, err := f.GetExpiredFuture(context.Background(), currency.NewPairWithDelimiter("BTC", "1231", "-")) + if err != nil { + t.Error(err) + } +} diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index 27f47bdfda5..c3963f1112f 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -120,38 +120,38 @@ type OHLCVData struct { // FuturesData stores data for futures type FuturesData struct { - Ask float64 `json:"ask"` - Bid float64 `json:"bid"` - Change1h float64 `json:"change1h"` - Change24h float64 `json:"change24h"` - ChangeBod float64 `json:"changeBod"` - VolumeUSD24h float64 `json:"volumeUsd24h"` - Volume float64 `json:"volume"` - Description string `json:"description"` - Enabled bool `json:"enabled"` - Expired bool `json:"expired"` - Expiry time.Time `json:"expiry"` - ExpiryDescription string `json:"expiryDescription"` - Group string `json:"group"` - Index float64 `json:"index"` - InitialMarginFractionFactor float64 `json:"imfFactor"` - Last float64 `json:"last"` - LowerBound float64 `json:"lowerBound"` - MarginPrice float64 `json:"marginPrice"` - Mark float64 `json:"mark"` - MoveStart interface{} `json:"moveStart"` - Name string `json:"name"` - OpenInterest float64 `json:"openInterest"` - OpenInterestUSD float64 `json:"openInterestUsd"` - Perpetual bool `json:"perpetual"` - PositionLimitWeight float64 `json:"positionLimitWeight"` - PostOnly bool `json:"postOnly"` - PriceIncrement float64 `json:"priceIncrement"` - SizeIncrement float64 `json:"sizeIncrement"` - Underlying string `json:"underlying"` - UnderlyingDescription string `json:"underlyingDescription"` - UpperBound float64 `json:"upperBound"` - FutureType string `json:"type"` + Ask float64 `json:"ask"` + Bid float64 `json:"bid"` + Change1h float64 `json:"change1h"` + Change24h float64 `json:"change24h"` + ChangeBod float64 `json:"changeBod"` + VolumeUSD24h float64 `json:"volumeUsd24h"` + Volume float64 `json:"volume"` + Description string `json:"description"` + Enabled bool `json:"enabled"` + Expired bool `json:"expired"` + Expiry time.Time `json:"expiry"` + ExpiryDescription string `json:"expiryDescription"` + Group string `json:"group"` + Index float64 `json:"index"` + InitialMarginFractionFactor float64 `json:"imfFactor"` + Last float64 `json:"last"` + LowerBound float64 `json:"lowerBound"` + MarginPrice float64 `json:"marginPrice"` + Mark float64 `json:"mark"` + MoveStart time.Time `json:"moveStart"` + Name string `json:"name"` + OpenInterest float64 `json:"openInterest"` + OpenInterestUSD float64 `json:"openInterestUsd"` + Perpetual bool `json:"perpetual"` + PositionLimitWeight float64 `json:"positionLimitWeight"` + PostOnly bool `json:"postOnly"` + PriceIncrement float64 `json:"priceIncrement"` + SizeIncrement float64 `json:"sizeIncrement"` + Underlying string `json:"underlying"` + UnderlyingDescription string `json:"underlyingDescription"` + UpperBound float64 `json:"upperBound"` + FutureType string `json:"type"` } // FutureStatsData stores data on futures stats @@ -759,35 +759,35 @@ type WsMarketsData struct { // WsMarketsDataStorage stores websocket markets data type WsMarketsDataStorage struct { - Name string `json:"name,omitempty"` - Enabled bool `json:"enabled,omitempty"` - PriceIncrement float64 `json:"priceIncrement,omitempty"` - SizeIncrement float64 `json:"sizeIncrement,omitempty"` - MarketType string `json:"marketType,omitempty"` - BaseCurrency string `json:"baseCurrency,omitempty"` - QuoteCurrency string `json:"quoteCurrency,omitempty"` - Underlying string `json:"underlying,omitempty"` - Restricted bool `json:"restricted,omitempty"` - Future WsMarketsFutureData `json:"future,omitempty"` + Name string `json:"name"` + Enabled bool `json:"enabled"` + PriceIncrement float64 `json:"priceIncrement"` + SizeIncrement float64 `json:"sizeIncrement"` + MarketType string `json:"marketType"` + BaseCurrency string `json:"baseCurrency"` + QuoteCurrency string `json:"quoteCurrency"` + Underlying string `json:"underlying"` + Restricted bool `json:"restricted"` + Future WsMarketsFutureData `json:"future"` } // WsMarketsFutureData stores websocket markets' future data type WsMarketsFutureData struct { - Name string `json:"name,omitempty"` - Underlying string `json:"underlying,omitempty"` - Description string `json:"description,omitempty"` - MarketType string `json:"type,omitempty"` - Expiry time.Time `json:"expiry,omitempty"` - Perpetual bool `json:"perpetual,omitempty"` - Expired bool `json:"expired,omitempty"` - Enabled bool `json:"enabled,omitempty"` - PostOnly bool `json:"postOnly,omitempty"` - InitialMarginFractionFactor float64 `json:"imfFactor,omitempty"` - UnderlyingDescription string `json:"underlyingDescription,omitempty"` - ExpiryDescription string `json:"expiryDescription,omitempty"` - MoveStart string `json:"moveStart,omitempty"` - PositionLimitWeight float64 `json:"positionLimitWeight,omitempty"` - Group string `json:"group,omitempty"` + Name string `json:"name"` + Underlying string `json:"underlying"` + Description string `json:"description"` + MarketType string `json:"type"` + Expiry time.Time `json:"expiry"` + Perpetual bool `json:"perpetual"` + Expired bool `json:"expired"` + Enabled bool `json:"enabled"` + PostOnly bool `json:"postOnly"` + InitialMarginFractionFactor float64 `json:"imfFactor"` + UnderlyingDescription string `json:"underlyingDescription"` + ExpiryDescription string `json:"expiryDescription"` + MoveStart string `json:"moveStart"` + PositionLimitWeight float64 `json:"positionLimitWeight"` + Group string `json:"group"` } // WSMarkets stores websocket markets data diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 7b36f0d6f5c..e16befee46d 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1328,6 +1328,9 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula // FTX bases scales all collateral into USD amounts return calc.CollateralAmount, nil } + if calc.USDPrice.IsZero() { + return decimal.Zero, fmt.Errorf("%s %s %w to scale collateral", f.Name, calc.CollateralCurrency, order.ErrUSDValueRequired) + } collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] if !ok { return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) @@ -1377,19 +1380,24 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula func (f *FTX) CalculateTotalCollateral(ctx context.Context, collateralAssets []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { var result order.TotalCollateralResponse for i := range collateralAssets { + curr := order.CollateralByCurrency{ + Currency: collateralAssets[i].CollateralCurrency, + } collateral, err := f.ScaleCollateral(ctx, &collateralAssets[i]) if err != nil { if errors.Is(err, errCollateralCurrencyNotFound) { log.Error(log.ExchangeSys, err) continue } + if errors.Is(err, order.ErrUSDValueRequired) { + curr.Error = err + result.BreakdownByCurrency = append(result.BreakdownByCurrency, curr) + continue + } return nil, err } result.TotalCollateral = result.TotalCollateral.Add(collateral) - curr := order.CollateralByCurrency{ - Currency: collateralAssets[i].CollateralCurrency, - Amount: collateral, - } + curr.Amount = collateral if !collateralAssets[i].CollateralCurrency.Match(currency.USD) { curr.ValueCurrency = currency.USD } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 0f64ad5629c..b63b3566a70 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -28,6 +28,8 @@ var ( ErrPositionLiquidated = errors.New("position liquidated") // ErrNotFuturesAsset returned when futures data is requested on a non-futures asset ErrNotFuturesAsset = errors.New("asset type is not futures") + // ErrUSDValueRequired returned when usd value unset + ErrUSDValueRequired = errors.New("USD value required") errExchangeNameEmpty = errors.New("exchange name empty") errTimeUnset = errors.New("time unset") @@ -70,6 +72,7 @@ type CollateralByCurrency struct { Currency currency.Code Amount decimal.Decimal ValueCurrency currency.Code + Error error } // PositionController manages all futures orders diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 3e0aeec4f7d..a47c208a751 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -10882,10 +10882,11 @@ type GetFuturesPositionsResponse struct { unknownFields protoimpl.UnknownFields TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` - TotalRealisedPNL string `protobuf:"bytes,2,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` - TotalUnrealisedPNL string `protobuf:"bytes,3,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` - TotalPNL string `protobuf:"bytes,4,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` - Positions []*FuturePosition `protobuf:"bytes,5,rep,name=positions,proto3" json:"positions,omitempty"` + SubAccount string `protobuf:"bytes,2,opt,name=subAccount,proto3" json:"subAccount,omitempty"` + TotalRealisedPNL string `protobuf:"bytes,3,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` + TotalUnrealisedPNL string `protobuf:"bytes,4,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` + TotalPNL string `protobuf:"bytes,5,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` + Positions []*FuturePosition `protobuf:"bytes,6,rep,name=positions,proto3" json:"positions,omitempty"` } func (x *GetFuturesPositionsResponse) Reset() { @@ -10927,6 +10928,13 @@ func (x *GetFuturesPositionsResponse) GetTotalOrders() int64 { return 0 } +func (x *GetFuturesPositionsResponse) GetSubAccount() string { + if x != nil { + return x.SubAccount + } + return "" +} + func (x *GetFuturesPositionsResponse) GetTotalRealisedPNL() string { if x != nil { return x.TotalRealisedPNL @@ -11134,8 +11142,9 @@ type GetCollateralResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TotalCollateral string `protobuf:"bytes,1,opt,name=totalCollateral,proto3" json:"totalCollateral,omitempty"` - CurrencyBreakdown []*CollateralForCurrency `protobuf:"bytes,2,rep,name=currencyBreakdown,proto3" json:"currencyBreakdown,omitempty"` + SubAccount string `protobuf:"bytes,1,opt,name=subAccount,proto3" json:"subAccount,omitempty"` + TotalCollateral string `protobuf:"bytes,2,opt,name=totalCollateral,proto3" json:"totalCollateral,omitempty"` + CurrencyBreakdown []*CollateralForCurrency `protobuf:"bytes,3,rep,name=currencyBreakdown,proto3" json:"currencyBreakdown,omitempty"` } func (x *GetCollateralResponse) Reset() { @@ -11170,6 +11179,13 @@ func (*GetCollateralResponse) Descriptor() ([]byte, []int) { return file_rpc_proto_rawDescGZIP(), []int{173} } +func (x *GetCollateralResponse) GetSubAccount() string { + if x != nil { + return x.SubAccount + } + return "" +} + func (x *GetCollateralResponse) GetTotalCollateral() string { if x != nil { return x.TotalCollateral @@ -11192,6 +11208,7 @@ type CollateralForCurrency struct { Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` ScaledCollateral string `protobuf:"bytes,2,opt,name=scaledCollateral,proto3" json:"scaledCollateral,omitempty"` ScaledToCurrency string `protobuf:"bytes,3,opt,name=scaledToCurrency,proto3" json:"scaledToCurrency,omitempty"` + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` } func (x *CollateralForCurrency) Reset() { @@ -11247,6 +11264,13 @@ func (x *CollateralForCurrency) GetScaledToCurrency() string { return "" } +func (x *CollateralForCurrency) GetError() string { + if x != nil { + return x.Error + } + return "" +} + type CancelBatchOrdersResponse_Orders struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -12806,19 +12830,21 @@ var file_rpc_proto_rawDesc = []byte{ 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, - 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x8d, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, 0x20, 0x01, + 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, + 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, - 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, - 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, + 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, @@ -12850,16 +12876,18 @@ var file_rpc_proto_rawDesc = []byte{ 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, - 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x15, 0x47, + 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, - 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x4b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, - 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, 0x0a, 0x15, + 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xa1, 0x01, 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, @@ -12868,723 +12896,724 @@ var file_rpc_proto_rawDesc = []byte{ 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, - 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, - 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, - 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, - 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, + 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, + 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, - 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, - 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, - 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, - 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, + 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, + 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, + 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, + 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, + 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, + 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, + 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, + 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, + 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, + 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, - 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, - 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, - 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, - 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, + 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, + 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, + 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, + 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, + 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, + 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, + 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, + 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, + 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, - 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, - 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, - 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, + 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, + 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, + 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, + 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, - 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, - 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, - 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, + 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, - 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, - 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, - 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, - 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, - 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, + 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, + 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, + 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, + 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, - 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, - 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, + 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, + 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, + 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, + 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, + 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, + 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, + 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, - 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, + 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, + 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, + 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, + 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, + 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, + 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, + 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, + 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, - 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, - 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, - 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, + 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, + 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, + 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, + 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, + 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, + 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, - 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, - 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, - 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, - 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, + 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, - 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, - 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, - 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, - 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, - 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, - 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, + 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, - 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, - 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, - 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, - 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, - 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, - 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, - 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, - 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, - 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, + 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, + 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, + 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, + 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, + 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, + 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, + 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, + 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, + 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, + 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, + 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, + 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, + 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, + 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, + 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, + 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, + 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, - 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, - 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, + 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, + 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, + 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, - 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, - 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, - 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, - 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, + 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, + 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, - 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, - 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, - 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, + 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, + 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, + 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, + 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 1b2e2a6c453..caf98a5a90d 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1037,10 +1037,11 @@ message GetFuturesPositionsRequest { message GetFuturesPositionsResponse { int64 totalOrders = 1; - string totalRealisedPNL = 2; - string totalUnrealisedPNL = 3; - string totalPNL = 4; - repeated FuturePosition positions = 5; + string subAccount = 2; + string totalRealisedPNL = 3; + string totalUnrealisedPNL = 4; + string totalPNL = 5; + repeated FuturePosition positions = 6; } message FuturePosition { @@ -1062,14 +1063,16 @@ message GetCollateralRequest { } message GetCollateralResponse { - string totalCollateral = 1; - repeated CollateralForCurrency currencyBreakdown = 2; + string subAccount = 1; + string totalCollateral = 2; + repeated CollateralForCurrency currencyBreakdown = 3; } message CollateralForCurrency { string currency = 1; string scaledCollateral = 2; string scaledToCurrency = 3; + string error = 4; } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 4910b48053f..ac35ca0e47c 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -3633,6 +3633,9 @@ }, "scaledToCurrency": { "type": "string" + }, + "error": { + "type": "string" } } }, @@ -4161,6 +4164,9 @@ "gctrpcGetCollateralResponse": { "type": "object", "properties": { + "subAccount": { + "type": "string" + }, "totalCollateral": { "type": "string" }, @@ -4390,6 +4396,9 @@ "type": "string", "format": "int64" }, + "subAccount": { + "type": "string" + }, "totalRealisedPNL": { "type": "string" }, From 665fcc95b7cbd207ed7284ecd7cb8275fe080b8e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 13 Jan 2022 12:06:03 +1100 Subject: [PATCH 010/171] Fixes oopsie --- engine/rpcserver.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 93292b47fa0..0762be2b7cc 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4316,9 +4316,8 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe return nil, err } - b := exch.GetBase() result := &gctrpc.GetCollateralResponse{ - SubAccount: b.API.Credentials.Subaccount, + SubAccount: r.SubAccount, TotalCollateral: collateral.TotalCollateral.String(), } if r.IncludeBreakdown { From 406d3c2d6d33b9f82949f45034096430e5d459c3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 13 Jan 2022 12:32:05 +1100 Subject: [PATCH 011/171] Fixes cool bug which allowed made up subaccount results --- engine/rpcserver.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 0762be2b7cc..0050f695766 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -69,6 +69,7 @@ var ( errCurrencyPairInvalid = errors.New("currency provided is not found in the available pairs list") errNoTrades = errors.New("no trades returned from supplied params") errNilRequestData = errors.New("nil request data received, cannot continue") + errNoAccountInformation = errors.New("account information does not exist") ) // RPCServer struct @@ -4278,16 +4279,19 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe return nil, err } var calculators []order.CollateralCalculator - var acc account.SubAccount + var acc *account.SubAccount if r.SubAccount != "" { for i := range ai.Accounts { if strings.EqualFold(r.SubAccount, ai.Accounts[i].ID) { - acc = ai.Accounts[i] + acc = &ai.Accounts[i] break } } } else if len(ai.Accounts) > 0 { - acc = ai.Accounts[0] + acc = &ai.Accounts[0] + } + if acc == nil { + return nil, fmt.Errorf("%w for %s %s and stored credentials", errNoAccountInformation, exch.GetName(), r.SubAccount) } for i := range acc.Currencies { cal := order.CollateralCalculator{ @@ -4315,9 +4319,14 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if err != nil { return nil, err } + subAccount := r.SubAccount + if subAccount == "" { + b := exch.GetBase() + subAccount = b.API.Credentials.Subaccount + } result := &gctrpc.GetCollateralResponse{ - SubAccount: r.SubAccount, + SubAccount: subAccount, TotalCollateral: collateral.TotalCollateral.String(), } if r.IncludeBreakdown { From 8f76c7809a4db789eb143618b23dcf70b4907759 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 14 Jan 2022 13:39:08 +1100 Subject: [PATCH 012/171] Subaccount override on FTX, subaccount results for collateral --- cmd/gctcli/commands.go | 26 +- engine/rpcserver.go | 5 +- engine/rpcserver_test.go | 2 +- exchanges/exchange.go | 4 +- exchanges/exchange_test.go | 4 +- exchanges/ftx/ftx.go | 169 ++-- exchanges/ftx/ftx_test.go | 86 +- exchanges/ftx/ftx_wrapper.go | 49 +- exchanges/order/futures_types.go | 4 +- gctrpc/rpc.pb.go | 1445 +++++++++++++++--------------- gctrpc/rpc.proto | 1 + gctrpc/rpc.swagger.json | 6 + 12 files changed, 940 insertions(+), 861 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 4a306bb3127..cb569857e8c 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4948,6 +4948,11 @@ var getCollateralCommand = &cli.Command{ Aliases: []string{"i"}, Usage: "include a list of each helds currency and its contribution to the overall collateral value", }, + &cli.BoolFlag{ + Name: "includezerovalues", + Aliases: []string{"z"}, + Usage: "include collateral values that are zero", + }, &cli.StringFlag{ Name: "subaccount", Aliases: []string{"s"}, @@ -5007,6 +5012,16 @@ func getCollateral(c *cli.Context) error { subAccount = c.Args().Get(4) } + var includeZeroValues bool + if c.IsSet("includezerovalues") { + includeZeroValues = c.Bool("includezerovalues") + } else if c.Args().Get(5) != "" { + includeZeroValues, err = strconv.ParseBool(c.Args().Get(5)) + if err != nil { + return err + } + } + conn, cancel, err := setupClient(c) if err != nil { return err @@ -5016,11 +5031,12 @@ func getCollateral(c *cli.Context) error { client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetCollateral(c.Context, &gctrpc.GetCollateralRequest{ - Exchange: exchangeName, - Asset: assetType, - SubAccount: subAccount, - IncludeBreakdown: includeBreakdown, - CalculateOffline: calculateOffline, + Exchange: exchangeName, + Asset: assetType, + SubAccount: subAccount, + IncludeBreakdown: includeBreakdown, + CalculateOffline: calculateOffline, + IncludeZeroValues: includeZeroValues, }) if err != nil { return err diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 0050f695766..f1575edae68 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4315,7 +4315,7 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe calculators = append(calculators, cal) } - collateral, err := exch.CalculateTotalCollateral(ctx, calculators) + collateral, err := exch.CalculateTotalCollateral(ctx, r.SubAccount, r.CalculateOffline, calculators) if err != nil { return nil, err } @@ -4331,6 +4331,9 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe } if r.IncludeBreakdown { for i := range collateral.BreakdownByCurrency { + if collateral.BreakdownByCurrency[i].Amount.IsZero() && !r.IncludeZeroValues { + continue + } cb := &gctrpc.CollateralForCurrency{ Currency: collateral.BreakdownByCurrency[i].Currency.String(), ScaledCollateral: collateral.BreakdownByCurrency[i].Amount.String(), diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 6f97f87bbc4..80a271dce09 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -132,7 +132,7 @@ func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp curre } // CalculateTotalCollateral overrides testExchange's CalculateTotalCollateral function -func (f fExchange) CalculateTotalCollateral(context.Context, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { +func (f fExchange) CalculateTotalCollateral(context.Context, string, bool, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { return &order.TotalCollateralResponse{ TotalCollateral: decimal.NewFromInt(1337), BreakdownByCurrency: []order.CollateralByCurrency{ diff --git a/exchanges/exchange.go b/exchanges/exchange.go index e793b9c2def..123b52fccb1 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1469,13 +1469,13 @@ func (b *Base) CalculatePNL(context.Context, *order.PNLCalculatorRequest) (*orde // ScaleCollateral is an overridable function to determine how much // collateral is usable in futures positions -func (b *Base) ScaleCollateral(context.Context, *order.CollateralCalculator) (decimal.Decimal, error) { +func (b *Base) ScaleCollateral(context.Context, string, *order.CollateralCalculator) (decimal.Decimal, error) { return decimal.Zero, common.ErrNotYetImplemented } // CalculateTotalCollateral takes in n collateral calculators to determine an overall // standing in a singular currency. See FTX's implementation -func (b *Base) CalculateTotalCollateral(context.Context, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { +func (b *Base) CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, calculators []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 0c210e598b8..feba2d86ba8 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -2476,7 +2476,7 @@ func TestCalculatePNL(t *testing.T) { func TestScaleCollateral(t *testing.T) { t.Parallel() var b Base - if _, err := b.ScaleCollateral(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) { + if _, err := b.ScaleCollateral(context.Background(), "", nil); !errors.Is(err, common.ErrNotYetImplemented) { t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) } } @@ -2484,7 +2484,7 @@ func TestScaleCollateral(t *testing.T) { func TestCalculateTotalCollateral(t *testing.T) { t.Parallel() var b Base - if _, err := b.CalculateTotalCollateral(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) { + if _, err := b.CalculateTotalCollateral(context.Background(), "", false, nil); !errors.Is(err, common.ErrNotYetImplemented) { t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) } } diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 0fa7c6ad30b..4cd24c8fdd4 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -312,21 +312,17 @@ func (f *FTX) GetFutureStats(ctx context.Context, futureName string) (FutureStat // GetExpiredFuture returns information on an expired futures contract func (f *FTX) GetExpiredFuture(ctx context.Context, pair currency.Pair) (FuturesData, error) { - resp := struct { - Data []FuturesData `json:"result"` - }{} - p, err := f.FormatSymbol(pair, asset.Futures) if err != nil { return FuturesData{}, err } - err = f.SendHTTPRequest(ctx, exchange.RestSpot, getExpiredFutures, &resp) + resp, err := f.GetExpiredFutures(ctx) if err != nil { return FuturesData{}, err } - for i := range resp.Data { - if resp.Data[i].Name == p { - return resp.Data[i], nil + for i := range resp { + if resp[i].Name == p { + return resp[i], nil } } return FuturesData{}, fmt.Errorf("%s %s %w", f.Name, p, currency.ErrPairNotFound) @@ -390,7 +386,7 @@ func (f *FTX) GetMarginBorrowRates(ctx context.Context) ([]MarginFundingData, er r := struct { Data []MarginFundingData `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginBorrowRates, nil, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginBorrowRates, "", nil, &r) } // GetMarginLendingRates gets lending rates for margin trading @@ -398,7 +394,7 @@ func (f *FTX) GetMarginLendingRates(ctx context.Context) ([]MarginFundingData, e r := struct { Data []MarginFundingData `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendingRates, nil, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendingRates, "", nil, &r) } // MarginDailyBorrowedAmounts gets daily borrowed amounts for margin @@ -414,7 +410,7 @@ func (f *FTX) GetMarginMarketInfo(ctx context.Context, market string) ([]MarginM r := struct { Data []MarginMarketInfo `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(marginMarketInfo, market), nil, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(marginMarketInfo, market), "", nil, &r) } // GetMarginBorrowHistory gets the margin borrow history data @@ -432,7 +428,7 @@ func (f *FTX) GetMarginBorrowHistory(ctx context.Context, startTime, endTime tim params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } endpoint := common.EncodeURLValues(marginBorrowHistory, params) - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &r) } // GetMarginMarketLendingHistory gets the markets margin lending rate history @@ -452,7 +448,7 @@ func (f *FTX) GetMarginMarketLendingHistory(ctx context.Context, coin currency.C params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } endpoint := common.EncodeURLValues(marginLendingHistory, params) - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, params, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", params, &r) } // GetMarginLendingHistory gets margin lending history @@ -472,7 +468,7 @@ func (f *FTX) GetMarginLendingHistory(ctx context.Context, coin currency.Code, s params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } endpoint := common.EncodeURLValues(marginLendHistory, params) - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendHistory, endpoint, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendHistory, "", endpoint, &r) } // GetMarginLendingOffers gets margin lending offers @@ -480,7 +476,7 @@ func (f *FTX) GetMarginLendingOffers(ctx context.Context) ([]LendingOffersData, r := struct { Data []LendingOffersData `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendingOffers, nil, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendingOffers, "", nil, &r) } // GetLendingInfo gets margin lending info @@ -488,7 +484,7 @@ func (f *FTX) GetLendingInfo(ctx context.Context) ([]LendingInfoData, error) { r := struct { Data []LendingInfoData `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendingInfo, nil, &r) + return r.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, marginLendingInfo, "", nil, &r) } // SubmitLendingOffer submits an offer for margin lending @@ -502,7 +498,7 @@ func (f *FTX) SubmitLendingOffer(ctx context.Context, coin currency.Code, size, req["size"] = size req["rate"] = rate - if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginLendingOffers, req, &resp); err != nil { + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, marginLendingOffers, "", req, &resp); err != nil { return err } @@ -513,11 +509,11 @@ func (f *FTX) SubmitLendingOffer(ctx context.Context, coin currency.Code, size, } // GetAccountInfo gets account info -func (f *FTX) GetAccountInfo(ctx context.Context) (AccountInfoData, error) { +func (f *FTX) GetAccountInfo(ctx context.Context, subAccount string) (AccountInfoData, error) { resp := struct { Data AccountInfoData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getAccountInfo, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getAccountInfo, subAccount, nil, &resp) } // GetPositions gets the users positions @@ -525,30 +521,30 @@ func (f *FTX) GetPositions(ctx context.Context) ([]PositionData, error) { resp := struct { Data []PositionData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getPositions, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getPositions, "", nil, &resp) } // ChangeAccountLeverage changes default leverage used by account func (f *FTX) ChangeAccountLeverage(ctx context.Context, leverage float64) error { req := make(map[string]interface{}) req["leverage"] = leverage - return f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, setLeverage, req, nil) + return f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, setLeverage, "", req, nil) } // GetCoins gets coins' data in the account wallet -func (f *FTX) GetCoins(ctx context.Context) ([]WalletCoinsData, error) { +func (f *FTX) GetCoins(ctx context.Context, subAccount string) ([]WalletCoinsData, error) { resp := struct { Data []WalletCoinsData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getCoins, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getCoins, subAccount, nil, &resp) } // GetBalances gets balances of the account -func (f *FTX) GetBalances(ctx context.Context) ([]WalletBalance, error) { +func (f *FTX) GetBalances(ctx context.Context, subAccount string) ([]WalletBalance, error) { resp := struct { Data []WalletBalance `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getBalances, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getBalances, subAccount, nil, &resp) } // GetAllWalletBalances gets all wallets' balances @@ -556,7 +552,7 @@ func (f *FTX) GetAllWalletBalances(ctx context.Context) (AllWalletBalances, erro resp := struct { Data AllWalletBalances `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getAllWalletBalances, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getAllWalletBalances, "", nil, &resp) } // FetchDepositAddress gets deposit address for a given coin @@ -569,7 +565,7 @@ func (f *FTX) FetchDepositAddress(ctx context.Context, coin currency.Code, chain vals.Set("method", strings.ToLower(chain)) } path := common.EncodeURLValues(getDepositAddress+coin.Upper().String(), vals) - return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, path, nil, &resp) + return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, path, "", nil, &resp) } // FetchDepositHistory gets deposit history @@ -577,7 +573,7 @@ func (f *FTX) FetchDepositHistory(ctx context.Context) ([]DepositItem, error) { resp := struct { Data []DepositItem `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getDepositHistory, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getDepositHistory, "", nil, &resp) } // FetchWithdrawalHistory gets withdrawal history @@ -585,7 +581,7 @@ func (f *FTX) FetchWithdrawalHistory(ctx context.Context) ([]WithdrawItem, error resp := struct { Data []WithdrawItem `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getWithdrawalHistory, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getWithdrawalHistory, "", nil, &resp) } // Withdraw sends a withdrawal request @@ -613,7 +609,7 @@ func (f *FTX) Withdraw(ctx context.Context, coin currency.Code, address, tag, pa resp := struct { Data WithdrawItem `json:"result"` }{} - return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, withdrawRequest, req, &resp) + return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, withdrawRequest, "", req, &resp) } // GetOpenOrders gets open orders @@ -626,7 +622,7 @@ func (f *FTX) GetOpenOrders(ctx context.Context, marketName string) ([]OrderData Data []OrderData `json:"result"` }{} endpoint := common.EncodeURLValues(getOpenOrders, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) } // FetchOrderHistory gets order history @@ -649,7 +645,7 @@ func (f *FTX) FetchOrderHistory(ctx context.Context, marketName string, startTim params.Set("limit", limit) } endpoint := common.EncodeURLValues(getOrderHistory, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) } // GetOpenTriggerOrders gets trigger orders that are currently open @@ -665,7 +661,7 @@ func (f *FTX) GetOpenTriggerOrders(ctx context.Context, marketName, orderType st Data []TriggerOrderData `json:"result"` }{} endpoint := common.EncodeURLValues(getOpenTriggerOrders, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) } // GetTriggerOrderTriggers gets trigger orders that are currently open @@ -673,7 +669,7 @@ func (f *FTX) GetTriggerOrderTriggers(ctx context.Context, orderID string) ([]Tr resp := struct { Data []TriggerData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(getTriggerOrderTriggers, orderID), nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(getTriggerOrderTriggers, orderID), "", nil, &resp) } // GetTriggerOrderHistory gets trigger orders that are currently open @@ -702,7 +698,7 @@ func (f *FTX) GetTriggerOrderHistory(ctx context.Context, marketName string, sta Data []TriggerOrderData `json:"result"` }{} endpoint := common.EncodeURLValues(getTriggerOrderHistory, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) } // Order places an order @@ -734,7 +730,7 @@ func (f *FTX) Order( resp := struct { Data OrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, placeOrder, req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, placeOrder, "", req, &resp) } // TriggerOrder places an order @@ -764,7 +760,7 @@ func (f *FTX) TriggerOrder(ctx context.Context, marketName, side, orderType, red resp := struct { Data TriggerOrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, placeTriggerOrder, req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, placeTriggerOrder, "", req, &resp) } // ModifyPlacedOrder modifies a placed order @@ -778,7 +774,7 @@ func (f *FTX) ModifyPlacedOrder(ctx context.Context, orderID, clientID string, p resp := struct { Data OrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyOrder, orderID), req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyOrder, orderID), "", req, &resp) } // ModifyOrderByClientID modifies a placed order via clientOrderID @@ -792,7 +788,7 @@ func (f *FTX) ModifyOrderByClientID(ctx context.Context, clientOrderID, clientID resp := struct { Data OrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyOrderByClientID, clientOrderID), req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyOrderByClientID, clientOrderID), "", req, &resp) } // ModifyTriggerOrder modifies an existing trigger order @@ -814,7 +810,7 @@ func (f *FTX) ModifyTriggerOrder(ctx context.Context, orderID, orderType string, resp := struct { Data TriggerOrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyTriggerOrder, orderID), req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyTriggerOrder, orderID), "", req, &resp) } // GetOrderStatus gets the order status of a given orderID @@ -822,7 +818,7 @@ func (f *FTX) GetOrderStatus(ctx context.Context, orderID string) (OrderData, er resp := struct { Data OrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOrderStatus+orderID, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOrderStatus+orderID, "", nil, &resp) } // GetOrderStatusByClientID gets the order status of a given clientOrderID @@ -830,7 +826,7 @@ func (f *FTX) GetOrderStatusByClientID(ctx context.Context, clientOrderID string resp := struct { Data OrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOrderStatusByClientID+clientOrderID, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOrderStatusByClientID+clientOrderID, "", nil, &resp) } func (f *FTX) deleteOrderByPath(ctx context.Context, path string) (string, error) { @@ -839,7 +835,7 @@ func (f *FTX) deleteOrderByPath(ctx context.Context, path string) (string, error Success bool `json:"success"` Error string `json:"error"` }{} - err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, path, nil, &resp) + err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, path, "", nil, &resp) // If there is an error reported, but the resp struct reports one of a very few // specific error causes, we still consider this a successful cancellation. if err != nil && !resp.Success && (resp.Error == "Order already closed" || resp.Error == "Order already queued for cancellation") { @@ -896,7 +892,7 @@ func (f *FTX) GetFills(ctx context.Context, market currency.Pair, item asset.Ite params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } endpoint := common.EncodeURLValues(getFills, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) } // GetFundingPayments gets funding payments @@ -916,7 +912,7 @@ func (f *FTX) GetFundingPayments(ctx context.Context, startTime, endTime time.Ti params.Set("future", future) } endpoint := common.EncodeURLValues(getFundingPayments, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) } // ListLeveragedTokens lists leveraged tokens @@ -924,7 +920,7 @@ func (f *FTX) ListLeveragedTokens(ctx context.Context) ([]LeveragedTokensData, e resp := struct { Data []LeveragedTokensData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLeveragedTokens, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLeveragedTokens, "", nil, &resp) } // GetTokenInfo gets token info @@ -932,7 +928,7 @@ func (f *FTX) GetTokenInfo(ctx context.Context, tokenName string) ([]LeveragedTo resp := struct { Data []LeveragedTokensData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getTokenInfo+tokenName, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getTokenInfo+tokenName, "", nil, &resp) } // ListLTBalances gets leveraged tokens' balances @@ -940,7 +936,7 @@ func (f *FTX) ListLTBalances(ctx context.Context) ([]LTBalanceData, error) { resp := struct { Data []LTBalanceData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLTBalances, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLTBalances, "", nil, &resp) } // ListLTCreations lists the leveraged tokens' creation requests @@ -948,7 +944,7 @@ func (f *FTX) ListLTCreations(ctx context.Context) ([]LTCreationData, error) { resp := struct { Data []LTCreationData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLTCreations, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLTCreations, "", nil, &resp) } // RequestLTCreation sends a request to create a leveraged token @@ -958,7 +954,7 @@ func (f *FTX) RequestLTCreation(ctx context.Context, tokenName string, size floa resp := struct { Data RequestTokenCreationData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(requestLTCreation, tokenName), req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(requestLTCreation, tokenName), "", req, &resp) } // ListLTRedemptions lists the leveraged tokens' redemption requests @@ -966,7 +962,7 @@ func (f *FTX) ListLTRedemptions(ctx context.Context) ([]LTRedemptionData, error) resp := struct { Data []LTRedemptionData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLTRedemptions, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getLTRedemptions, "", nil, &resp) } // RequestLTRedemption sends a request to redeem a leveraged token @@ -976,7 +972,7 @@ func (f *FTX) RequestLTRedemption(ctx context.Context, tokenName string, size fl resp := struct { Data LTRedemptionRequestData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(requestLTRedemption, tokenName), req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(requestLTRedemption, tokenName), "", req, &resp) } // GetQuoteRequests gets a list of quote requests @@ -984,7 +980,7 @@ func (f *FTX) GetQuoteRequests(ctx context.Context) ([]QuoteRequestData, error) resp := struct { Data []QuoteRequestData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getListQuotes, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getListQuotes, "", nil, &resp) } // GetYourQuoteRequests gets a list of your quote requests @@ -992,7 +988,7 @@ func (f *FTX) GetYourQuoteRequests(ctx context.Context) ([]PersonalQuotesData, e resp := struct { Data []PersonalQuotesData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getMyQuotesRequests, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getMyQuotesRequests, "", nil, &resp) } // CreateQuoteRequest sends a request to create a quote @@ -1017,7 +1013,7 @@ func (f *FTX) CreateQuoteRequest(ctx context.Context, underlying currency.Code, resp := struct { Data CreateQuoteRequestData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, createQuoteRequest, req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, createQuoteRequest, "", req, &resp) } // DeleteQuote sends request to cancel a quote @@ -1025,13 +1021,13 @@ func (f *FTX) DeleteQuote(ctx context.Context, requestID string) (CancelQuoteReq resp := struct { Data CancelQuoteRequestData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, deleteQuote+requestID, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, deleteQuote+requestID, "", nil, &resp) } // GetQuotesForYourQuote gets a list of quotes for your quote func (f *FTX) GetQuotesForYourQuote(ctx context.Context, requestID string) (QuoteForQuoteData, error) { var resp QuoteForQuoteData - return resp, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(endpointQuote, requestID), nil, &resp) + return resp, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(endpointQuote, requestID), "", nil, &resp) } // MakeQuote makes a quote for a quote @@ -1041,7 +1037,7 @@ func (f *FTX) MakeQuote(ctx context.Context, requestID, price string) ([]QuoteFo resp := struct { Data []QuoteForQuoteData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(endpointQuote, requestID), nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(endpointQuote, requestID), "", nil, &resp) } // MyQuotes gets a list of my quotes for quotes @@ -1049,7 +1045,7 @@ func (f *FTX) MyQuotes(ctx context.Context) ([]QuoteForQuoteData, error) { resp := struct { Data []QuoteForQuoteData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getMyQuotes, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getMyQuotes, "", nil, &resp) } // DeleteMyQuote deletes my quote for quotes @@ -1057,7 +1053,7 @@ func (f *FTX) DeleteMyQuote(ctx context.Context, quoteID string) ([]QuoteForQuot resp := struct { Data []QuoteForQuoteData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, deleteMyQuote+quoteID, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, deleteMyQuote+quoteID, "", nil, &resp) } // AcceptQuote accepts the quote for quote @@ -1065,7 +1061,7 @@ func (f *FTX) AcceptQuote(ctx context.Context, quoteID string) ([]QuoteForQuoteD resp := struct { Data []QuoteForQuoteData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(acceptQuote, quoteID), nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(acceptQuote, quoteID), "", nil, &resp) } // GetAccountOptionsInfo gets account's options' info @@ -1073,7 +1069,7 @@ func (f *FTX) GetAccountOptionsInfo(ctx context.Context) (AccountOptionsInfoData resp := struct { Data AccountOptionsInfoData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOptionsInfo, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOptionsInfo, "", nil, &resp) } // GetOptionsPositions gets options' positions @@ -1081,7 +1077,7 @@ func (f *FTX) GetOptionsPositions(ctx context.Context) ([]OptionsPositionsData, resp := struct { Data []OptionsPositionsData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOptionsPositions, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOptionsPositions, "", nil, &resp) } // GetPublicOptionsTrades gets options' trades from public @@ -1120,7 +1116,7 @@ func (f *FTX) GetOptionsFills(ctx context.Context, startTime, endTime time.Time, if limit != "" { req["limit"] = limit } - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOptionsFills, req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOptionsFills, "", req, &resp) } // GetStakes returns a list of staked assets @@ -1128,7 +1124,7 @@ func (f *FTX) GetStakes(ctx context.Context) ([]Stake, error) { resp := struct { Data []Stake `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, stakes, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, stakes, "", nil, &resp) } // GetUnstakeRequests returns a collection of unstake requests @@ -1136,7 +1132,7 @@ func (f *FTX) GetUnstakeRequests(ctx context.Context) ([]UnstakeRequest, error) resp := struct { Data []UnstakeRequest `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, unstakeRequests, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, unstakeRequests, "", nil, &resp) } // GetStakeBalances returns a collection of staked coin balances @@ -1144,7 +1140,7 @@ func (f *FTX) GetStakeBalances(ctx context.Context) ([]StakeBalance, error) { resp := struct { Data []StakeBalance `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, stakeBalances, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, stakeBalances, "", nil, &resp) } // UnstakeRequest unstakes an existing staked coin @@ -1155,7 +1151,7 @@ func (f *FTX) UnstakeRequest(ctx context.Context, coin currency.Code, size float req := make(map[string]interface{}) req["coin"] = coin.Upper().String() req["size"] = strconv.FormatFloat(size, 'f', -1, 64) - return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, unstakeRequests, req, &resp) + return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, unstakeRequests, "", req, &resp) } // CancelUnstakeRequest cancels a pending unstake request @@ -1164,7 +1160,7 @@ func (f *FTX) CancelUnstakeRequest(ctx context.Context, requestID int64) (bool, Result string }{} path := unstakeRequests + "/" + strconv.FormatInt(requestID, 10) - if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, path, nil, &resp); err != nil { + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, path, "", nil, &resp); err != nil { return false, err } @@ -1179,7 +1175,7 @@ func (f *FTX) GetStakingRewards(ctx context.Context) ([]StakeReward, error) { resp := struct { Data []StakeReward `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, stakingRewards, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, stakingRewards, "", nil, &resp) } // StakeRequest submits a stake request based on the specified currency and size @@ -1190,11 +1186,11 @@ func (f *FTX) StakeRequest(ctx context.Context, coin currency.Code, size float64 req := make(map[string]interface{}) req["coin"] = coin.Upper().String() req["size"] = strconv.FormatFloat(size, 'f', -1, 64) - return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, serumStakes, req, &resp) + return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, serumStakes, "", req, &resp) } // SendAuthHTTPRequest sends an authenticated request -func (f *FTX) SendAuthHTTPRequest(ctx context.Context, ep exchange.URL, method, path string, data, result interface{}) error { +func (f *FTX) SendAuthHTTPRequest(ctx context.Context, ep exchange.URL, method, path, subAccount string, data, result interface{}) error { if !f.AllowAuthenticatedRequest() { return fmt.Errorf("%s %w", f.Name, exchange.ErrAuthenticatedRequestWithoutCredentialsSet) } @@ -1230,8 +1226,11 @@ func (f *FTX) SendAuthHTTPRequest(ctx context.Context, ep exchange.URL, method, headers["FTX-KEY"] = f.API.Credentials.Key headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac) headers["FTX-TS"] = ts - if f.API.Credentials.Subaccount != "" { - headers["FTX-SUBACCOUNT"] = url.QueryEscape(f.API.Credentials.Subaccount) + if subAccount == "" && f.API.Credentials.Subaccount != "" { + subAccount = f.API.Credentials.Subaccount + } + if subAccount != "" { + headers["FTX-SUBACCOUNT"] = url.QueryEscape(subAccount) } headers["Content-Type"] = "application/json" @@ -1260,7 +1259,7 @@ func (f *FTX) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (floa case exchange.OfflineTradeFee: fee = getOfflineTradeFee(feeBuilder) default: - feeData, err := f.GetAccountInfo(ctx) + feeData, err := f.GetAccountInfo(ctx, "") if err != nil { return 0, err } @@ -1345,7 +1344,7 @@ func (f *FTX) RequestForQuotes(ctx context.Context, base, quote currency.Code, a req["fromCoin"] = base.Upper().String() req["toCoin"] = quote.Upper().String() req["size"] = amount - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, requestOTCQuote, req, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, requestOTCQuote, "", req, &resp) } // GetOTCQuoteStatus gets quote status of a quote @@ -1355,12 +1354,12 @@ func (f *FTX) GetOTCQuoteStatus(ctx context.Context, marketName, quoteID string) }{} params := url.Values{} params.Set("market", marketName) - return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOTCQuoteStatus+quoteID, params, &resp) + return &resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getOTCQuoteStatus+quoteID, "", params, &resp) } // AcceptOTCQuote requests for otc quotes func (f *FTX) AcceptOTCQuote(ctx context.Context, quoteID string) error { - return f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(acceptOTCQuote, quoteID), nil, nil) + return f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, fmt.Sprintf(acceptOTCQuote, quoteID), "", nil, nil) } // GetSubaccounts returns the users subaccounts @@ -1368,7 +1367,7 @@ func (f *FTX) GetSubaccounts(ctx context.Context) ([]Subaccount, error) { resp := struct { Data []Subaccount `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, subaccounts, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, subaccounts, "", nil, &resp) } // CreateSubaccount creates a new subaccount @@ -1382,7 +1381,7 @@ func (f *FTX) CreateSubaccount(ctx context.Context, name string) (*Subaccount, e resp := struct { Data Subaccount `json:"result"` }{} - if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, subaccounts, d, &resp); err != nil { + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, subaccounts, "", d, &resp); err != nil { return nil, err } return &resp.Data, nil @@ -1400,7 +1399,7 @@ func (f *FTX) UpdateSubaccountName(ctx context.Context, oldName, newName string) resp := struct { Data Subaccount `json:"result"` }{} - if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, subaccountsUpdateName, d, &resp); err != nil { + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, subaccountsUpdateName, "", d, &resp); err != nil { return nil, err } return &resp.Data, nil @@ -1416,7 +1415,7 @@ func (f *FTX) DeleteSubaccount(ctx context.Context, name string) error { resp := struct { Data Subaccount `json:"result"` }{} - return f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, subaccounts, d, &resp) + return f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, subaccounts, "", d, &resp) } // SubaccountBalances returns the user's subaccount balances @@ -1427,7 +1426,7 @@ func (f *FTX) SubaccountBalances(ctx context.Context, name string) ([]Subaccount resp := struct { Data []SubaccountBalance `json:"result"` }{} - if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(subaccountsBalance, name), nil, &resp); err != nil { + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(subaccountsBalance, name), "", nil, &resp); err != nil { return nil, err } return resp.Data, nil @@ -1458,7 +1457,7 @@ func (f *FTX) SubaccountTransfer(ctx context.Context, coin currency.Code, source resp := struct { Data SubaccountTransferStatus `json:"result"` }{} - if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, subaccountsTransfer, d, &resp); err != nil { + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, subaccountsTransfer, "", d, &resp); err != nil { return nil, err } return &resp.Data, nil @@ -1644,7 +1643,7 @@ func (f *FTX) LoadCollateralWeightings(ctx context.Context) error { if !f.GetAuthenticatedAPISupport(exchange.RestAuthentication) { return nil } - coins, err := f.GetCoins(ctx) + coins, err := f.GetCoins(ctx, "") if err != nil { return err } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index d629793450e..8dd26009602 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -264,7 +264,7 @@ func TestGetAccountInfo(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetAccountInfo(context.Background()) + _, err := f.GetAccountInfo(context.Background(), subaccount) if err != nil { t.Error(err) } @@ -286,7 +286,7 @@ func TestGetBalances(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetBalances(context.Background()) + _, err := f.GetBalances(context.Background(), subaccount) if err != nil { t.Error(err) } @@ -319,7 +319,7 @@ func TestGetCoins(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetCoins(context.Background()) + _, err := f.GetCoins(context.Background(), subaccount) if err != nil { t.Error(err) } @@ -1739,7 +1739,7 @@ func TestScaleCollateral(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - accountInfo, err := f.GetAccountInfo(context.Background()) + accountInfo, err := f.GetAccountInfo(context.Background(), subaccount) if err != nil { t.Error(err) } @@ -1765,14 +1765,17 @@ func TestScaleCollateral(t *testing.T) { t.Error(err) } var scaled decimal.Decimal - scaled, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), - Asset: asset.Spot, - Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.NewFromFloat(tick.Price), - CalculateOffline: true, - }) + scaled, err = f.ScaleCollateral( + context.Background(), + "", + &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + CalculateOffline: true, + }) if err != nil { if errors.Is(err, errCollateralCurrencyNotFound) || errors.Is(err, order.ErrUSDValueRequired) { @@ -1783,38 +1786,45 @@ func TestScaleCollateral(t *testing.T) { localScaling += scaled.InexactFloat64() providedUSDValue += v[v2].USDValue - scaled, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), - Asset: asset.Spot, - Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.NewFromFloat(tick.Price), - IsLiquidating: true, - CalculateOffline: true, - }) + scaled, err = f.ScaleCollateral(context.Background(), + subaccount, + &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + IsLiquidating: true, + CalculateOffline: true, + }) if err != nil { t.Error(err) } liquidationScaling += scaled.InexactFloat64() - _, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), - Asset: asset.Spot, - Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.Zero, - IsLiquidating: true, - CalculateOffline: true, - }) + _, err = f.ScaleCollateral(context.Background(), + subaccount, + &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.Zero, + IsLiquidating: true, + CalculateOffline: true, + }) if !errors.Is(err, order.ErrUSDValueRequired) { t.Errorf("received '%v' exepected '%v'", err, order.ErrUSDValueRequired) } - _, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), - Asset: asset.Spot, - Side: order.Buy, - }) + _, err = f.ScaleCollateral( + context.Background(), + "", + &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + }) if err != nil { t.Error(err) } @@ -1868,12 +1878,12 @@ func TestCalculateTotalCollateral(t *testing.T) { }) } } - total, err := f.CalculateTotalCollateral(context.Background(), scales) + total, err := f.CalculateTotalCollateral(context.Background(), subaccount, true, scales) if err != nil { t.Error(err) } localScaling := total.TotalCollateral.InexactFloat64() - accountInfo, err := f.GetAccountInfo(context.Background()) + accountInfo, err := f.GetAccountInfo(context.Background(), subaccount) if err != nil { t.Error(err) } @@ -1884,7 +1894,7 @@ func TestCalculateTotalCollateral(t *testing.T) { for i := range scales { scales[i].CalculateOffline = false } - _, err = f.CalculateTotalCollateral(context.Background(), scales) + _, err = f.CalculateTotalCollateral(context.Background(), subaccount, false, scales) if err != nil { t.Error(err) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index e16befee46d..8f280e89d75 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -450,7 +450,7 @@ func (f *FTX) UpdateAccountInfo(ctx context.Context, a asset.Item) (account.Hold var data AllWalletBalances if f.API.Credentials.Subaccount != "" { - balances, err := f.GetBalances(ctx) + balances, err := f.GetBalances(ctx, "") if err != nil { return resp, err } @@ -1251,7 +1251,7 @@ func (f *FTX) UpdateOrderExecutionLimits(ctx context.Context, _ asset.Item) erro // GetAvailableTransferChains returns the available transfer blockchains for the specific // cryptocurrency func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) { - coins, err := f.GetCoins(ctx) + coins, err := f.GetCoins(ctx, "") if err != nil { return nil, err } @@ -1286,7 +1286,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) } ep := pnl.EntryPrice.InexactFloat64() - info, err := f.GetAccountInfo(ctx) + info, err := f.GetAccountInfo(ctx, "") if err != nil { return nil, err } @@ -1321,7 +1321,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) } // ScaleCollateral takes your totals and scales them according to FTX's rules -func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalculator) (decimal.Decimal, error) { +func (f *FTX) ScaleCollateral(ctx context.Context, subAccount string, calc *order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal if calc.CalculateOffline { if calc.CollateralCurrency.Match(currency.USD) { @@ -1352,11 +1352,11 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula } return result, nil } - wallet, err := f.GetCoins(ctx) + wallet, err := f.GetCoins(ctx, subAccount) if err != nil { return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, err) } - balances, err := f.GetBalances(ctx) + balances, err := f.GetBalances(ctx, subAccount) if err != nil { return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, err) } @@ -1377,13 +1377,46 @@ func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalcula } // CalculateTotalCollateral scales collateral and determines how much collateral you can use for positions -func (f *FTX) CalculateTotalCollateral(ctx context.Context, collateralAssets []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { +func (f *FTX) CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, collateralAssets []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { var result order.TotalCollateralResponse + if !calculateOffline { + wallet, err := f.GetCoins(ctx, subAccount) + if err != nil { + return nil, fmt.Errorf("%s %w", f.Name, err) + } + balances, err := f.GetBalances(ctx, subAccount) + if err != nil { + return nil, fmt.Errorf("%s %w", f.Name, err) + } + for x := range collateralAssets { + wallets: + for y := range wallet { + if !currency.NewCode(wallet[y].ID).Match(collateralAssets[x].CollateralCurrency) { + continue + } + for z := range balances { + if !currency.NewCode(balances[z].Coin).Match(collateralAssets[x].CollateralCurrency) { + continue + } + scaled := wallet[y].CollateralWeight * balances[z].USDValue + dScaled := decimal.NewFromFloat(scaled) + result.TotalCollateral = result.TotalCollateral.Add(dScaled) + result.BreakdownByCurrency = append(result.BreakdownByCurrency, order.CollateralByCurrency{ + Currency: collateralAssets[x].CollateralCurrency, + Amount: dScaled, + ValueCurrency: currency.USD, + }) + break wallets + } + } + } + return &result, nil + } for i := range collateralAssets { curr := order.CollateralByCurrency{ Currency: collateralAssets[i].CollateralCurrency, } - collateral, err := f.ScaleCollateral(ctx, &collateralAssets[i]) + collateral, err := f.ScaleCollateral(ctx, subAccount, &collateralAssets[i]) if err != nil { if errors.Is(err, errCollateralCurrencyNotFound) { log.Error(log.ExchangeSys, err) diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index b63b3566a70..9c6897698b8 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -54,8 +54,8 @@ type PNLCalculation interface { // multiple ways of calculating the size of collateral // on an exchange type CollateralManagement interface { - ScaleCollateral(context.Context, *CollateralCalculator) (decimal.Decimal, error) - CalculateTotalCollateral(context.Context, []CollateralCalculator) (*TotalCollateralResponse, error) + ScaleCollateral(context.Context, string, *CollateralCalculator) (decimal.Decimal, error) + CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, collaterals []CollateralCalculator) (*TotalCollateralResponse, error) } // TotalCollateralResponse holds all collateral diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index a47c208a751..477bcdb407c 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -11063,11 +11063,12 @@ type GetCollateralRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` - SubAccount string `protobuf:"bytes,3,opt,name=subAccount,proto3" json:"subAccount,omitempty"` - IncludeBreakdown bool `protobuf:"varint,4,opt,name=includeBreakdown,proto3" json:"includeBreakdown,omitempty"` - CalculateOffline bool `protobuf:"varint,5,opt,name=calculateOffline,proto3" json:"calculateOffline,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + SubAccount string `protobuf:"bytes,3,opt,name=subAccount,proto3" json:"subAccount,omitempty"` + IncludeBreakdown bool `protobuf:"varint,4,opt,name=includeBreakdown,proto3" json:"includeBreakdown,omitempty"` + CalculateOffline bool `protobuf:"varint,5,opt,name=calculateOffline,proto3" json:"calculateOffline,omitempty"` + IncludeZeroValues bool `protobuf:"varint,6,opt,name=includeZeroValues,proto3" json:"includeZeroValues,omitempty"` } func (x *GetCollateralRequest) Reset() { @@ -11137,6 +11138,13 @@ func (x *GetCollateralRequest) GetCalculateOffline() bool { return false } +func (x *GetCollateralRequest) GetIncludeZeroValues() bool { + if x != nil { + return x.IncludeZeroValues + } + return false +} + type GetCollateralResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -12864,7 +12872,7 @@ var file_rpc_proto_rawDesc = []byte{ 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, - 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, + 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xee, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, @@ -12876,744 +12884,747 @@ var file_rpc_proto_rawDesc = []byte{ 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, - 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x15, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, - 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x4b, - 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, - 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xa1, 0x01, 0x0a, 0x15, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, - 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, - 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, - 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, - 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, + 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5a, 0x65, 0x72, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5a, 0x65, + 0x72, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, + 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x4b, 0x0a, 0x11, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, + 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xa1, 0x01, 0x0a, 0x15, 0x43, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x10, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x83, 0x59, + 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, + 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, + 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, + 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, + 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, - 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, - 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, - 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, - 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, - 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, - 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, - 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, - 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, - 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, - 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, - 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, + 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, + 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, + 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, + 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, + 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, + 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, - 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, - 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, + 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, - 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, + 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, + 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, - 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, - 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, - 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, - 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, + 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, + 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, + 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, - 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, + 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, + 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, - 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, - 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, + 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, + 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, + 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, + 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, + 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, - 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, - 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, - 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, - 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, - 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, - 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, - 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, + 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, + 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, + 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, + 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, + 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, + 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, + 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, + 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, - 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, + 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, + 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, + 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, + 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, + 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, + 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, - 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, - 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, - 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, - 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, - 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, - 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, - 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, - 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, - 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, + 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, + 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, + 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, + 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, + 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, + 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, + 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, + 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, - 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, - 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, - 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, - 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, - 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, - 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, - 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, - 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, - 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, - 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, - 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, - 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, - 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, - 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, + 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, + 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, + 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, + 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, + 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, + 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, + 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, - 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, - 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, - 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, - 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, - 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, - 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, - 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, - 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, - 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, + 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, + 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, + 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, + 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, + 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, + 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, + 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, + 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, + 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, + 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, + 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, + 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, + 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, + 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, + 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, + 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, + 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, - 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, - 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, + 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, + 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, - 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, - 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, + 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index caf98a5a90d..e09eb669d48 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1060,6 +1060,7 @@ message GetCollateralRequest { string subAccount = 3; bool includeBreakdown = 4; bool calculateOffline = 5; + bool includeZeroValues = 6; } message GetCollateralResponse { diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index ac35ca0e47c..a01edb9ad76 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1206,6 +1206,12 @@ "in": "query", "required": false, "type": "boolean" + }, + { + "name": "includeZeroValues", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ From f276cffa216baa5556fdbc48d31f78ddec7c2e0a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 17 Jan 2022 11:49:25 +1100 Subject: [PATCH 013/171] Strenghten collateral account info checks. Improve FTX test --- cmd/gctcli/commands.go | 6 +++--- engine/rpcserver.go | 30 ++++++++++++++++-------------- exchanges/ftx/ftx_test.go | 6 +++--- exchanges/ftx/ftx_wrapper.go | 4 +++- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index cb569857e8c..28238d733c6 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4925,7 +4925,7 @@ func getFuturesPositions(c *cli.Context) error { var getCollateralCommand = &cli.Command{ Name: "getcollateral", Usage: "returns total collateral for an exchange asset, with optional per currency breakdown", - ArgsUsage: " ", + ArgsUsage: " ", Action: getCollateral, Flags: []cli.Flag{ &cli.StringFlag{ @@ -4946,7 +4946,7 @@ var getCollateralCommand = &cli.Command{ &cli.BoolFlag{ Name: "includebreakdown", Aliases: []string{"i"}, - Usage: "include a list of each helds currency and its contribution to the overall collateral value", + Usage: "include a list of each held currency and its contribution to the overall collateral value", }, &cli.BoolFlag{ Name: "includezerovalues", @@ -4956,7 +4956,7 @@ var getCollateralCommand = &cli.Command{ &cli.StringFlag{ Name: "subaccount", Aliases: []string{"s"}, - Usage: "the subaccount to retreieve collateral data from, depending on individual exchange support", + Usage: "the subaccount to retrieve collateral data from, depending on individual exchange support", }, }, } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index f1575edae68..9a3ea056dbb 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4280,18 +4280,25 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe } var calculators []order.CollateralCalculator var acc *account.SubAccount - if r.SubAccount != "" { - for i := range ai.Accounts { - if strings.EqualFold(r.SubAccount, ai.Accounts[i].ID) { - acc = &ai.Accounts[i] - break - } + var subAccounts []string + subAccount := r.SubAccount + if subAccount == "" { + b := exch.GetBase() + subAccount = b.API.Credentials.Subaccount + } + for i := range ai.Accounts { + subAccounts = append(subAccounts, ai.Accounts[i].ID) + if ai.Accounts[i].ID == "main" && subAccount == "" { + acc = &ai.Accounts[i] + break + } + if strings.EqualFold(subAccount, ai.Accounts[i].ID) { + acc = &ai.Accounts[i] + break } - } else if len(ai.Accounts) > 0 { - acc = &ai.Accounts[0] } if acc == nil { - return nil, fmt.Errorf("%w for %s %s and stored credentials", errNoAccountInformation, exch.GetName(), r.SubAccount) + return nil, fmt.Errorf("%w for %s %s and stored credentials - available subaccounts: %s", errNoAccountInformation, exch.GetName(), r.SubAccount, strings.Join(subAccounts, ",")) } for i := range acc.Currencies { cal := order.CollateralCalculator{ @@ -4319,11 +4326,6 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if err != nil { return nil, err } - subAccount := r.SubAccount - if subAccount == "" { - b := exch.GetBase() - subAccount = b.API.Credentials.Subaccount - } result := &gctrpc.GetCollateralResponse{ SubAccount: subAccount, diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 8dd26009602..c7d3bc6b26c 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -984,12 +984,12 @@ func TestGetPublicOptionsTrades(t *testing.T) { } tmNow := time.Now() result, err = f.GetPublicOptionsTrades(context.Background(), - tmNow.AddDate(0, 0, -7), tmNow, "5") + tmNow.AddDate(0, 0, -30), tmNow, "5") if err != nil { t.Error(err) } - if len(result) != 5 { - t.Error("limit of 5 should return 5 items") + if len(result) > 5 { + t.Error("limit of 5 should max of 5 items") } _, err = f.GetPublicOptionsTrades(context.Background(), time.Unix(validFTTBTCEndTime, 0), time.Unix(validFTTBTCStartTime, 0), "5") diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 8f280e89d75..d9d024e6d76 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -471,7 +471,9 @@ func (f *FTX) UpdateAccountInfo(ctx context.Context, a asset.Item) (account.Hold var acc = account.SubAccount{ID: subName, AssetType: a} for x := range balances { c := currency.NewCode(balances[x].Coin) - hold := balances[x].Total - balances[x].Free + // the Free field includes borrow amount with available holdings + // Using AvailableWithoutBorrow allows for a more accurate picture of balance + hold := balances[x].Total - balances[x].AvailableWithoutBorrow acc.Currencies = append(acc.Currencies, account.Balance{CurrencyName: c, TotalValue: balances[x].Total, From 1d0d9e089e2e2b41b173f1d14e441ecc8547bfac Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 17 Jan 2022 11:55:07 +1100 Subject: [PATCH 014/171] English is my first language --- exchanges/ftx/ftx_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index c7d3bc6b26c..8909e8efc37 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -989,7 +989,7 @@ func TestGetPublicOptionsTrades(t *testing.T) { t.Error(err) } if len(result) > 5 { - t.Error("limit of 5 should max of 5 items") + t.Error("limit of 5 should return a max of 5 items") } _, err = f.GetPublicOptionsTrades(context.Background(), time.Unix(validFTTBTCEndTime, 0), time.Unix(validFTTBTCStartTime, 0), "5") From 13b4878051944e228f032be555053f412a4e8c11 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 17 Jan 2022 12:06:37 +1100 Subject: [PATCH 015/171] Fixes oopsies --- engine/rpcserver_test.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 80a271dce09..dc9b6fe4b57 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -2125,8 +2125,8 @@ func TestGetCollateral(t *testing.T) { b.Enabled = true cp, err := currency.NewPairFromString("btc-usd") - if err != nil { - t.Fatal(err) + if !errors.Is(err, nil) { + t.Fatalf("received '%v', expected '%v'", err, nil) } b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) @@ -2155,25 +2155,22 @@ func TestGetCollateral(t *testing.T) { }, } - r, err := s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + _, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ Exchange: fakeExchangeName, Asset: asset.Futures.String(), }) - if err != nil { - t.Error(err) - } - if len(r.CurrencyBreakdown) > 0 { - t.Error("expected no breakdown") + if !errors.Is(err, errNoAccountInformation) { + t.Fatalf("received '%v', expected '%v'", err, errNoAccountInformation) } - r, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + r, err := s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ Exchange: fakeExchangeName, Asset: asset.Futures.String(), IncludeBreakdown: true, SubAccount: "1337", }) - if err != nil { - t.Error(err) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) } if len(r.CurrencyBreakdown) != 3 { t.Error("expected 3 currencies") From d2ee82c28d1ea3d3955d739302efc9b146385574 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 3 Nov 2021 16:41:21 +1100 Subject: [PATCH 016/171] Adds some conceptual futures order details to track PNL --- backtester/backtest/backtest.go | 5 + backtester/common/common_types.go | 2 + backtester/config/config_types.go | 1 + .../portfolio/compliance/compliance_types.go | 3 +- .../eventhandlers/portfolio/portfolio.go | 26 ++- .../portfolio/portfolio_types.go | 2 + .../eventhandlers/portfolio/risk/risk.go | 2 +- .../statistics/currencystatistics.go | 4 +- .../strategies/futures/futures.go | 173 ++++++++++++++ .../strategies/futures/futures_test.go | 220 ++++++++++++++++++ backtester/report/report.go | 10 +- exchanges/order/order_types.go | 21 ++ 12 files changed, 459 insertions(+), 10 deletions(-) create mode 100644 backtester/eventhandlers/strategies/futures/futures.go create mode 100644 backtester/eventhandlers/strategies/futures/futures_test.go diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index a099194e828..9ab01403601 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -992,6 +992,11 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if err != nil { log.Error(log.BackTester, err) } + + err = bt.Portfolio.CalculatePNL(ev) + if err != nil { + log.Error(log.BackTester, err) + } return nil } diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 4fabbb331b5..e63f85d679b 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -26,6 +26,8 @@ const ( // MissingData is signalled during the strategy/signal phase when data has been identified as missing // No buy or sell events can occur MissingData order.Side = "MISSING DATA" + Long order.Side = "LONG" + Short order.Side = "SHORT" // CandleStr is a config readable data type to tell the backtester to retrieve candle data CandleStr = "candle" // TradeStr is a config readable data type to tell the backtester to retrieve trade data diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index a68401481e0..c7157a0583e 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -75,6 +75,7 @@ type ExchangeLevelFunding struct { Currency string `json:"currency"` InitialFunds decimal.Decimal `json:"initial-funds"` TransferFee decimal.Decimal `json:"transfer-fee"` + Collateral bool `json:"collateral"` } // StatisticSettings adjusts ratios where diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index 51584ff7d6e..ae5a7bca37f 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -33,5 +33,6 @@ type SnapshotOrder struct { VolumeAdjustedPrice decimal.Decimal `json:"volume-adjusted-price"` SlippageRate decimal.Decimal `json:"slippage-rate"` CostBasis decimal.Decimal `json:"cost-basis"` - *order.Detail `json:"order-detail"` + SpotOrder *order.Detail `json:"order-detail"` + FuturesOrder *order.Futures `json:"futures-order"` } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 1af839f7634..70bacf32264 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -295,7 +295,7 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { ClosePrice: fillEvent.GetClosePrice(), VolumeAdjustedPrice: fillEvent.GetVolumeAdjustedPrice(), SlippageRate: fillEvent.GetSlippageRate(), - Detail: fo, + SpotOrder: fo, CostBasis: price.Mul(amount).Add(fee), } prevSnap.Orders = append(prevSnap.Orders, snapOrder) @@ -477,3 +477,27 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { } return holdings.Holding{} } + +// CalculatePNL will analyse any futures orders that have been placed over the backtesting run +// that are not closed and calculate their PNL +func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { + orders, err := p.GetLatestOrderSnapshotForEvent(e) + if err != nil { + return err + } + for i := range orders.Orders { + if orders.Orders[i].FuturesOrder == nil { + continue + } + if orders.Orders[i].FuturesOrder.ClosingPosition != nil { + continue + } + + openPrice := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Price) + openAmount := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Amount) + changeInPosition := e.ClosePrice().Sub(openPrice).Mul(openAmount) + orders.Orders[i].FuturesOrder.UnrealisedPNL = changeInPosition + orders.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = changeInPosition + } + return nil +} diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index c7ab69054e4..b2bcd6af17d 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -57,6 +57,8 @@ type Handler interface { SetFee(string, asset.Item, currency.Pair, decimal.Decimal) GetFee(string, asset.Item, currency.Pair) decimal.Decimal + CalculatePNL(common.DataEventHandler) error + Reset() } diff --git a/backtester/eventhandlers/portfolio/risk/risk.go b/backtester/eventhandlers/portfolio/risk/risk.go index d66e799c641..6dc4a63560b 100644 --- a/backtester/eventhandlers/portfolio/risk/risk.go +++ b/backtester/eventhandlers/portfolio/risk/risk.go @@ -59,7 +59,7 @@ func existingLeverageRatio(s compliance.Snapshot) decimal.Decimal { } var ordersWithLeverage decimal.Decimal for o := range s.Orders { - if s.Orders[o].Leverage != 0 { + if s.Orders[o].SpotOrder.Leverage != 0 { ordersWithLeverage = ordersWithLeverage.Add(decimal.NewFromInt(1)) } } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 254cd73b921..97ddc178074 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -28,9 +28,9 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e last := c.Events[len(c.Events)-1] lastPrice := last.DataEvent.GetClosePrice() for i := range last.Transactions.Orders { - if last.Transactions.Orders[i].Side == gctorder.Buy { + if last.Transactions.Orders[i].SpotOrder.Side == gctorder.Buy { c.BuyOrders++ - } else if last.Transactions.Orders[i].Side == gctorder.Sell { + } else if last.Transactions.Orders[i].SpotOrder.Side == gctorder.Sell { c.SellOrders++ } } diff --git a/backtester/eventhandlers/strategies/futures/futures.go b/backtester/eventhandlers/strategies/futures/futures.go new file mode 100644 index 00000000000..06b1480949c --- /dev/null +++ b/backtester/eventhandlers/strategies/futures/futures.go @@ -0,0 +1,173 @@ +package rsi + +import ( + "fmt" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gct-ta/indicators" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" + "github.com/thrasher-corp/gocryptotrader/backtester/funding" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" +) + +const ( + // Name is the strategy name + Name = "futures-rsi" + rsiPeriodKey = "rsi-period" + rsiLowKey = "rsi-low" + rsiHighKey = "rsi-high" + description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` +) + +// Strategy is an implementation of the Handler interface +type Strategy struct { + base.Strategy + rsiPeriod decimal.Decimal + rsiLow decimal.Decimal + rsiHigh decimal.Decimal +} + +// Name returns the name of the strategy +func (s *Strategy) Name() string { + return Name +} + +// Description provides a nice overview of the strategy +// be it definition of terms or to highlight its purpose +func (s *Strategy) Description() string { + return description +} + +// OnSignal handles a data event and returns what action the strategy believes should occur +// For rsi, this means returning a buy signal when rsi is at or below a certain level, and a +// sell signal when it is at or above a certain level +func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfolio.Handler) (signal.Event, error) { + if d == nil { + return nil, common.ErrNilEvent + } + es, err := s.GetBaseData(d) + if err != nil { + return nil, err + } + es.SetPrice(d.Latest().ClosePrice()) + + if offset := d.Offset(); offset <= int(s.rsiPeriod.IntPart()) { + es.AppendReason("Not enough data for signal generation") + es.SetDirection(common.DoNothing) + return &es, nil + } + + dataRange := d.StreamClose() + var massagedData []float64 + massagedData, err = s.massageMissingData(dataRange, es.GetTime()) + if err != nil { + return nil, err + } + rsi := indicators.RSI(massagedData, int(s.rsiPeriod.IntPart())) + latestRSIValue := decimal.NewFromFloat(rsi[len(rsi)-1]) + if !d.HasDataAtTime(d.Latest().GetTime()) { + es.SetDirection(common.MissingData) + es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions. RSI %v", d.Latest().GetTime(), latestRSIValue)) + return &es, nil + } + + currentOrders, err := p.GetLatestOrderSnapshotForEvent(&es) + if err != nil { + return nil, err + } + + currentOrders.Orders + + switch { + case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): + es.SetDirection(common.Short) + case latestRSIValue.LessThanOrEqual(s.rsiLow): + es.SetDirection(common.Long) + default: + es.SetDirection(common.DoNothing) + } + es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) + + return &es, nil +} + +// SupportsSimultaneousProcessing highlights whether the strategy can handle multiple currency calculation +// There is nothing actually stopping this strategy from considering multiple currencies at once +// but for demonstration purposes, this strategy does not +func (s *Strategy) SupportsSimultaneousProcessing() bool { + return false +} + +// OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility +// in allowing a strategy to only place an order for X currency if Y currency's price is Z +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { + return nil, base.ErrSimultaneousProcessingNotSupported +} + +// SetCustomSettings allows a user to modify the RSI limits in their config +func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) error { + for k, v := range customSettings { + switch k { + case rsiHighKey: + rsiHigh, ok := v.(float64) + if !ok || rsiHigh <= 0 { + return fmt.Errorf("%w provided rsi-high value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.rsiHigh = decimal.NewFromFloat(rsiHigh) + case rsiLowKey: + rsiLow, ok := v.(float64) + if !ok || rsiLow <= 0 { + return fmt.Errorf("%w provided rsi-low value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.rsiLow = decimal.NewFromFloat(rsiLow) + case rsiPeriodKey: + rsiPeriod, ok := v.(float64) + if !ok || rsiPeriod <= 0 { + return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.rsiPeriod = decimal.NewFromFloat(rsiPeriod) + default: + return fmt.Errorf("%w unrecognised custom setting key %v with value %v. Cannot apply", base.ErrInvalidCustomSettings, k, v) + } + } + + return nil +} + +// SetDefaults sets the custom settings to their default values +func (s *Strategy) SetDefaults() { + s.rsiHigh = decimal.NewFromInt(70) + s.rsiLow = decimal.NewFromInt(30) + s.rsiPeriod = decimal.NewFromInt(14) +} + +// massageMissingData will replace missing data with the previous candle's data +// this will ensure that RSI can be calculated correctly +// the decision to handle missing data occurs at the strategy level, not all strategies +// may wish to modify data +func (s *Strategy) massageMissingData(data []decimal.Decimal, t time.Time) ([]float64, error) { + var resp []float64 + var missingDataStreak int64 + for i := range data { + if data[i].IsZero() && i > int(s.rsiPeriod.IntPart()) { + data[i] = data[i-1] + missingDataStreak++ + } else { + missingDataStreak = 0 + } + if missingDataStreak >= s.rsiPeriod.IntPart() { + return nil, fmt.Errorf("missing data exceeds RSI period length of %v at %s and will distort results. %w", + s.rsiPeriod, + t.Format(gctcommon.SimpleTimeFormat), + base.ErrTooMuchBadData) + } + d, _ := data[i].Float64() + resp = append(resp, d) + } + return resp, nil +} diff --git a/backtester/eventhandlers/strategies/futures/futures_test.go b/backtester/eventhandlers/strategies/futures/futures_test.go new file mode 100644 index 00000000000..377183d1ef8 --- /dev/null +++ b/backtester/eventhandlers/strategies/futures/futures_test.go @@ -0,0 +1,220 @@ +package rsi + +import ( + "errors" + "strings" + "testing" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + eventkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" +) + +func TestName(t *testing.T) { + t.Parallel() + d := Strategy{} + if n := d.Name(); n != Name { + t.Errorf("expected %v", Name) + } +} + +func TestSupportsSimultaneousProcessing(t *testing.T) { + t.Parallel() + s := Strategy{} + if !s.SupportsSimultaneousProcessing() { + t.Error("expected true") + } +} + +func TestSetCustomSettings(t *testing.T) { + t.Parallel() + s := Strategy{} + err := s.SetCustomSettings(nil) + if err != nil { + t.Error(err) + } + float14 := float64(14) + mappalopalous := make(map[string]interface{}) + mappalopalous[rsiPeriodKey] = float14 + mappalopalous[rsiLowKey] = float14 + mappalopalous[rsiHighKey] = float14 + + err = s.SetCustomSettings(mappalopalous) + if err != nil { + t.Error(err) + } + + mappalopalous[rsiPeriodKey] = "14" + err = s.SetCustomSettings(mappalopalous) + if !errors.Is(err, base.ErrInvalidCustomSettings) { + t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) + } + + mappalopalous[rsiPeriodKey] = float14 + mappalopalous[rsiLowKey] = "14" + err = s.SetCustomSettings(mappalopalous) + if !errors.Is(err, base.ErrInvalidCustomSettings) { + t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) + } + + mappalopalous[rsiLowKey] = float14 + mappalopalous[rsiHighKey] = "14" + err = s.SetCustomSettings(mappalopalous) + if !errors.Is(err, base.ErrInvalidCustomSettings) { + t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) + } + + mappalopalous[rsiHighKey] = float14 + mappalopalous["lol"] = float14 + err = s.SetCustomSettings(mappalopalous) + if !errors.Is(err, base.ErrInvalidCustomSettings) { + t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) + } +} + +func TestOnSignal(t *testing.T) { + t.Parallel() + s := Strategy{} + _, err := s.OnSignal(nil, nil, nil) + if !errors.Is(err, common.ErrNilEvent) { + t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) + } + dStart := time.Date(2020, 1, 0, 0, 0, 0, 0, time.UTC) + dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + dEnd := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) + exch := "binance" + a := asset.Spot + p := currency.NewPair(currency.BTC, currency.USDT) + d := data.Base{} + d.SetStream([]common.DataEventHandler{&eventkline.Kline{ + Base: event.Base{ + Offset: 3, + Exchange: exch, + Time: dInsert, + Interval: gctkline.OneDay, + CurrencyPair: p, + AssetType: a, + }, + Open: decimal.NewFromInt(1337), + Close: decimal.NewFromInt(1337), + Low: decimal.NewFromInt(1337), + High: decimal.NewFromInt(1337), + Volume: decimal.NewFromInt(1337), + }}, + ) + d.Next() + da := &kline.DataFromKline{ + Item: gctkline.Item{}, + Base: d, + RangeHolder: &gctkline.IntervalRangeHolder{}, + } + var resp signal.Event + _, err = s.OnSignal(da, nil, nil) + if !errors.Is(err, base.ErrTooMuchBadData) { + t.Fatalf("expected: %v, received %v", base.ErrTooMuchBadData, err) + } + + s.rsiPeriod = decimal.NewFromInt(1) + _, err = s.OnSignal(da, nil, nil) + if err != nil { + t.Error(err) + } + + da.Item = gctkline.Item{ + Exchange: exch, + Pair: p, + Asset: a, + Interval: gctkline.OneDay, + Candles: []gctkline.Candle{ + { + Time: dInsert, + Open: 1337, + High: 1337, + Low: 1337, + Close: 1337, + Volume: 1337, + }, + }, + } + err = da.Load() + if err != nil { + t.Error(err) + } + + ranger, err := gctkline.CalculateCandleDateRanges(dStart, dEnd, gctkline.OneDay, 100000) + if err != nil { + t.Error(err) + } + da.RangeHolder = ranger + da.RangeHolder.SetHasDataFromCandles(da.Item.Candles) + resp, err = s.OnSignal(da, nil, nil) + if err != nil { + t.Error(err) + } + if resp.GetDirection() != common.DoNothing { + t.Error("expected do nothing") + } +} + +func TestOnSignals(t *testing.T) { + t.Parallel() + s := Strategy{} + _, err := s.OnSignal(nil, nil, nil) + if !errors.Is(err, common.ErrNilEvent) { + t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) + } + dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + exch := "binance" + a := asset.Spot + p := currency.NewPair(currency.BTC, currency.USDT) + d := data.Base{} + d.SetStream([]common.DataEventHandler{&eventkline.Kline{ + Base: event.Base{ + Exchange: exch, + Time: dInsert, + Interval: gctkline.OneDay, + CurrencyPair: p, + AssetType: a, + }, + Open: decimal.NewFromInt(1337), + Close: decimal.NewFromInt(1337), + Low: decimal.NewFromInt(1337), + High: decimal.NewFromInt(1337), + Volume: decimal.NewFromInt(1337), + }}) + d.Next() + da := &kline.DataFromKline{ + Item: gctkline.Item{}, + Base: d, + RangeHolder: &gctkline.IntervalRangeHolder{}, + } + _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) + if !strings.Contains(err.Error(), base.ErrTooMuchBadData.Error()) { + // common.Errs type doesn't keep type + t.Errorf("received: %v, expected: %v", err, base.ErrTooMuchBadData) + } +} + +func TestSetDefaults(t *testing.T) { + t.Parallel() + s := Strategy{} + s.SetDefaults() + if !s.rsiHigh.Equal(decimal.NewFromInt(70)) { + t.Error("expected 70") + } + if !s.rsiLow.Equal(decimal.NewFromInt(30)) { + t.Error("expected 30") + } + if !s.rsiPeriod.Equal(decimal.NewFromInt(14)) { + t.Error("expected 14") + } +} diff --git a/backtester/report/report.go b/backtester/report/report.go index 3be4ab40b2e..152aa7eb412 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -223,15 +223,15 @@ func (d *Data) enhanceCandles() error { } } for k := range statsForCandles.FinalOrders.Orders { - if statsForCandles.FinalOrders.Orders[k].Detail == nil || - !statsForCandles.FinalOrders.Orders[k].Date.Equal(d.OriginalCandles[intVal].Candles[j].Time) { + if statsForCandles.FinalOrders.Orders[k].SpotOrder == nil || + !statsForCandles.FinalOrders.Orders[k].SpotOrder.Date.Equal(d.OriginalCandles[intVal].Candles[j].Time) { continue } // an order was placed here, can enhance chart! enhancedCandle.MadeOrder = true - enhancedCandle.OrderAmount = decimal.NewFromFloat(statsForCandles.FinalOrders.Orders[k].Amount) - enhancedCandle.PurchasePrice = statsForCandles.FinalOrders.Orders[k].Price - enhancedCandle.OrderDirection = statsForCandles.FinalOrders.Orders[k].Side + enhancedCandle.OrderAmount = decimal.NewFromFloat(statsForCandles.FinalOrders.Orders[k].SpotOrder.Amount) + enhancedCandle.PurchasePrice = statsForCandles.FinalOrders.Orders[k].SpotOrder.Price + enhancedCandle.OrderDirection = statsForCandles.FinalOrders.Orders[k].SpotOrder.Side if enhancedCandle.OrderDirection == order.Buy { enhancedCandle.Colour = "green" enhancedCandle.Position = "aboveBar" diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 0d395bd864e..9fad526bfb3 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -4,6 +4,7 @@ import ( "errors" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -114,6 +115,23 @@ type ModifyResponse struct { OrderID string } +// Futures order is a concept which holds both the opening and closing orders +// for a futures contract. This allows for PNL calculations +type Futures struct { + Side Side + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal + OpeningPosition *Detail + ClosingPosition *Detail + PNLHistory []PNLHistory +} + +type PNLHistory struct { + Price decimal.Decimal + Amount decimal.Decimal + UnrealisedPNL decimal.Decimal +} + // Detail contains all properties of an order // Each exchange has their own requirements, so not all fields // are required to be populated @@ -146,6 +164,7 @@ type Detail struct { Type Type Side Side Status Status + UnrealisedPNL decimal.Decimal AssetType asset.Item Date time.Time CloseTime time.Time @@ -295,6 +314,8 @@ const ( Bid Side = "BID" Ask Side = "ASK" UnknownSide Side = "UNKNOWN" + Long Side = "LONG" + Short Side = "SHORT" ) // ByPrice used for sorting orders by price From 197bd9e07d568aaf2235cb2d10231a0b44909216 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 4 Nov 2021 16:38:09 +1100 Subject: [PATCH 017/171] Initial design of future order processing in the backtester --- backtester/common/common_types.go | 2 - backtester/eventhandlers/exchange/exchange.go | 1 + .../eventhandlers/portfolio/portfolio.go | 38 +++++- .../eventhandlers/portfolio/portfolio_test.go | 105 +++++++++++++++- .../strategies/futures/futures.go | 112 +++++++++++++++--- backtester/eventtypes/fill/fill.go | 6 + backtester/eventtypes/fill/fill_types.go | 2 + backtester/eventtypes/order/order.go | 6 + backtester/eventtypes/order/order_types.go | 6 + backtester/eventtypes/signal/signal.go | 25 ++++ backtester/eventtypes/signal/signal_types.go | 24 +++- backtester/report/tpl.gohtml | 10 +- exchanges/order/order_types.go | 19 ++- 13 files changed, 318 insertions(+), 38 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index e63f85d679b..4fabbb331b5 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -26,8 +26,6 @@ const ( // MissingData is signalled during the strategy/signal phase when data has been identified as missing // No buy or sell events can occur MissingData order.Side = "MISSING DATA" - Long order.Side = "LONG" - Short order.Side = "SHORT" // CandleStr is a config readable data type to tell the backtester to retrieve candle data CandleStr = "candle" // TradeStr is a config readable data type to tell the backtester to retrieve trade data diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 36b18bd4d85..2ae8f1881ff 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -41,6 +41,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * Direction: o.GetDirection(), Amount: o.GetAmount(), ClosePrice: data.Latest().GetClosePrice(), + LinkedOrderID: o.GetLinkedOrderID(), } eventFunds := o.GetAllocatedFunds() cs, err := e.GetCurrencySettings(o.GetExchange(), o.GetAssetType(), o.Pair()) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 70bacf32264..604ffa96f45 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -99,7 +99,8 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi Interval: ev.GetInterval(), Reason: ev.GetReason(), }, - Direction: ev.GetDirection(), + Direction: ev.GetDirection(), + LinkedOrderID: ev.GetLinkedOrderID(), } if ev.GetDirection() == "" { return o, errInvalidDirection @@ -295,10 +296,29 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { ClosePrice: fillEvent.GetClosePrice(), VolumeAdjustedPrice: fillEvent.GetVolumeAdjustedPrice(), SlippageRate: fillEvent.GetSlippageRate(), - SpotOrder: fo, CostBasis: price.Mul(amount).Add(fee), } - prevSnap.Orders = append(prevSnap.Orders, snapOrder) + if fo.AssetType == asset.Spot { + snapOrder.SpotOrder = fo + prevSnap.Orders = append(prevSnap.Orders, snapOrder) + } else if fo.AssetType == asset.Futures { + var linked bool + for i := range prevSnap.Orders { + if prevSnap.Orders[i].FuturesOrder != nil && + prevSnap.Orders[i].FuturesOrder.OpeningPosition != nil && + prevSnap.Orders[i].FuturesOrder.OpeningPosition.ID == fillEvent.GetLinkedOrderID() { + prevSnap.Orders[i].FuturesOrder.ClosingPosition = fo + linked = true + } + } + if !linked { + snapOrder.FuturesOrder = &gctorder.Futures{ + Side: fillEvent.GetDirection(), + OpeningPosition: fo, + } + prevSnap.Orders = append(prevSnap.Orders, snapOrder) + } + } } return complianceManager.AddSnapshot(prevSnap.Orders, fillEvent.GetTime(), fillEvent.GetOffset(), false) } @@ -493,11 +513,21 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { continue } + if orders.Orders[i].FuturesOrder.OpeningPosition.Leverage == 0 { + orders.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 + } + leverage := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Leverage) openPrice := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Price) openAmount := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Amount) - changeInPosition := e.ClosePrice().Sub(openPrice).Mul(openAmount) + + changeInPosition := e.ClosePrice().Sub(openPrice).Mul(openAmount).Mul(leverage) orders.Orders[i].FuturesOrder.UnrealisedPNL = changeInPosition orders.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = changeInPosition + orders.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ + Time: e.GetTime(), + UnrealisedPNL: changeInPosition, + }) + } return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index e7132c868d4..ba410c51946 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -579,7 +579,7 @@ func TestGetSnapshotAtTime(t *testing.T) { tt := time.Now() err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { - Detail: &gctorder.Detail{ + SpotOrder: &gctorder.Detail{ Exchange: "exch", AssetType: asset.Spot, Pair: cp, @@ -607,7 +607,7 @@ func TestGetSnapshotAtTime(t *testing.T) { if len(ss.Orders) != 1 { t.Fatal("expected 1") } - if ss.Orders[0].Amount != 1337 { + if ss.Orders[0].SpotOrder.Amount != 1337 { t.Error("expected 1") } } @@ -627,7 +627,7 @@ func TestGetLatestSnapshot(t *testing.T) { tt := time.Now() err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { - Detail: &gctorder.Detail{ + SpotOrder: &gctorder.Detail{ Exchange: "exch", AssetType: asset.Spot, Pair: cp, @@ -646,7 +646,7 @@ func TestGetLatestSnapshot(t *testing.T) { err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ ss[0].Orders[0], { - Detail: &gctorder.Detail{ + SpotOrder: &gctorder.Detail{ Exchange: "exch", AssetType: asset.Spot, Pair: cp, @@ -669,3 +669,100 @@ func TestGetLatestSnapshot(t *testing.T) { t.Error("expected 2") } } + +func TestCalculatePNL(t *testing.T) { + p := &Portfolio{ + riskFreeRate: decimal.Decimal{}, + sizeManager: nil, + riskManager: nil, + exchangeAssetPairSettings: nil, + } + + ev := &kline.Kline{} + err := p.CalculatePNL(ev) + if !errors.Is(err, errNoPortfolioSettings) { + t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) + } + + exch := "binance" + a := asset.Futures + pair, _ := currency.NewPairFromStrings("BTC", "1231") + s, err := p.SetupCurrencySettingsMap(&exchange.Settings{ + Exchange: exch, + UseRealOrders: false, + Pair: pair, + Asset: a, + }) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + tt := time.Now() + tt0 := time.Now().Add(-time.Hour) + ev.Exchange = exch + ev.AssetType = a + ev.CurrencyPair = pair + ev.Time = tt0 + + err = p.CalculatePNL(ev) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + + futuresOrder := &gctorder.Futures{ + Side: gctorder.Short, + OpeningPosition: &gctorder.Detail{ + Price: 1336, + Amount: 20, + Exchange: exch, + Side: gctorder.Short, + AssetType: asset.Futures, + Date: tt0, + Pair: pair, + }, + } + + ev.Close = decimal.NewFromInt(1337) + err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ + { + ClosePrice: decimal.NewFromInt(1336), + FuturesOrder: futuresOrder, + }, + }, tt0, 0, false) + err = p.CalculatePNL(ev) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + + if len(futuresOrder.PNLHistory) == 0 { + t.Error("expected a pnl entry ( ͡° ͜ʖ ͡°)") + } + + if !futuresOrder.UnrealisedPNL.Equal(decimal.NewFromInt(20)) { + // 20 orders * $1 difference * 1x leverage + t.Error("expected 20") + } + + err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ + { + ClosePrice: decimal.NewFromInt(1336), + SpotOrder: futuresOrder.OpeningPosition, + }, + }, tt, 1, false) + err = p.CalculatePNL(ev) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + + // coverage of logic + futuresOrder.ClosingPosition = futuresOrder.OpeningPosition + err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ + { + ClosePrice: decimal.NewFromInt(1336), + FuturesOrder: futuresOrder, + }, + }, tt.Add(time.Hour), 2, false) + err = p.CalculatePNL(ev) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } +} diff --git a/backtester/eventhandlers/strategies/futures/futures.go b/backtester/eventhandlers/strategies/futures/futures.go index 06b1480949c..fbb6e7194be 100644 --- a/backtester/eventhandlers/strategies/futures/futures.go +++ b/backtester/eventhandlers/strategies/futures/futures.go @@ -1,6 +1,7 @@ package rsi import ( + "errors" "fmt" "time" @@ -13,23 +14,35 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" gctcommon "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) const ( // Name is the strategy name - Name = "futures-rsi" - rsiPeriodKey = "rsi-period" - rsiLowKey = "rsi-low" - rsiHighKey = "rsi-high" - description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` + Name = "futures-rsi" + rsiPeriodKey = "rsi-period" + rsiLowKey = "rsi-low" + rsiHighKey = "rsi-high" + rsiStopLoss = "rsi-stop-loss" + rsiTakeProfit = "rsi-take-profit" + rsiTrailingStop = "rsi-trailing-stop" + rsiHighestUnrealised = "rsi-highest-unrealised" + rsiLowestUnrealised = "rsi-lowest-unrealised" + description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` ) // Strategy is an implementation of the Handler interface type Strategy struct { base.Strategy - rsiPeriod decimal.Decimal - rsiLow decimal.Decimal - rsiHigh decimal.Decimal + rsiPeriod decimal.Decimal + rsiLow decimal.Decimal + rsiHigh decimal.Decimal + stopLoss decimal.Decimal + takeProfit decimal.Decimal + trailingStop decimal.Decimal + highestUnrealised decimal.Decimal + lowestUnrealised decimal.Decimal } // Name returns the name of the strategy @@ -50,6 +63,11 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol if d == nil { return nil, common.ErrNilEvent } + latest := d.Latest() + if latest.GetAssetType() != asset.Futures { + return nil, errors.New("can only work with futures") + } + es, err := s.GetBaseData(d) if err != nil { return nil, err @@ -81,17 +99,45 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol return nil, err } - currentOrders.Orders - - switch { - case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): - es.SetDirection(common.Short) - case latestRSIValue.LessThanOrEqual(s.rsiLow): - es.SetDirection(common.Long) - default: - es.SetDirection(common.DoNothing) + var unrealisedOrder *order.Detail + for i := range currentOrders.Orders { + if currentOrders.Orders[i].FuturesOrder != nil { + if currentOrders.Orders[i].FuturesOrder.ClosingPosition == nil { + if currentOrders.Orders[i].FuturesOrder.Side == order.Short || currentOrders.Orders[i].FuturesOrder.Side == order.Long { + unrealisedOrder = currentOrders.Orders[i].FuturesOrder.OpeningPosition + } + } + } + } + if unrealisedOrder == nil { + switch { + case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): + es.SetDirection(order.Short) + case latestRSIValue.LessThanOrEqual(s.rsiLow): + es.SetDirection(order.Long) + default: + es.SetDirection(common.DoNothing) + } + es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) + } else { + p := decimal.NewFromFloat(unrealisedOrder.Price) + if latestRSIValue.LessThanOrEqual(s.rsiLow) || + latestRSIValue.GreaterThanOrEqual(s.rsiHigh) || + (!s.stopLoss.IsZero() && latest.ClosePrice().LessThanOrEqual(s.stopLoss)) || + (!s.takeProfit.IsZero() && latest.ClosePrice().GreaterThanOrEqual(s.takeProfit)) || + (!s.trailingStop.IsZero() && latest.ClosePrice().Sub(p).Div(p).Mul(decimal.NewFromInt(100)).LessThanOrEqual(s.trailingStop)) || + unrealisedOrder.UnrealisedPNL.GreaterThanOrEqual(s.highestUnrealised) || + unrealisedOrder.UnrealisedPNL.LessThanOrEqual(s.lowestUnrealised) { + // set up the counter order to close the position + es.SetAmount(decimal.NewFromFloat(unrealisedOrder.Amount)) + if unrealisedOrder.Side == order.Short { + es.SetDirection(order.Long) + } else if unrealisedOrder.Side == order.Long { + es.SetDirection(order.Short) + } + es.SetCloseOrderID(unrealisedOrder.ID) + } } - es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) return &es, nil } @@ -131,6 +177,36 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) } s.rsiPeriod = decimal.NewFromFloat(rsiPeriod) + case rsiStopLoss: + sl, ok := v.(float64) + if !ok || sl <= 0 { + return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.stopLoss = decimal.NewFromFloat(sl) + case rsiTakeProfit: + tp, ok := v.(float64) + if !ok || tp <= 0 { + return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.takeProfit = decimal.NewFromFloat(tp) + case rsiTrailingStop: + ts, ok := v.(float64) + if !ok || ts <= 0 { + return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.trailingStop = decimal.NewFromFloat(ts) + case rsiHighestUnrealised: + ts, ok := v.(float64) + if !ok || ts <= 0 { + return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.highestUnrealised = decimal.NewFromFloat(ts) + case rsiLowestUnrealised: + ts, ok := v.(float64) + if !ok || ts <= 0 { + return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.lowestUnrealised = decimal.NewFromFloat(ts) default: return fmt.Errorf("%w unrecognised custom setting key %v with value %v. Cannot apply", base.ErrInvalidCustomSettings, k, v) } diff --git a/backtester/eventtypes/fill/fill.go b/backtester/eventtypes/fill/fill.go index 609c6225ced..61d2d333fa7 100644 --- a/backtester/eventtypes/fill/fill.go +++ b/backtester/eventtypes/fill/fill.go @@ -64,3 +64,9 @@ func (f *Fill) GetOrder() *order.Detail { func (f *Fill) GetSlippageRate() decimal.Decimal { return f.Slippage } + +// GetLinkedOrderID returns the order ID of a linked +// futures order +func (f *Fill) GetLinkedOrderID() string { + return f.LinkedOrderID +} diff --git a/backtester/eventtypes/fill/fill_types.go b/backtester/eventtypes/fill/fill_types.go index f172357a267..55129c4ac76 100644 --- a/backtester/eventtypes/fill/fill_types.go +++ b/backtester/eventtypes/fill/fill_types.go @@ -19,6 +19,7 @@ type Fill struct { ExchangeFee decimal.Decimal `json:"exchange-fee"` Slippage decimal.Decimal `json:"slippage"` Order *order.Detail `json:"-"` + LinkedOrderID string `json:"linked-order-id"` } // Event holds all functions required to handle a fill event @@ -36,4 +37,5 @@ type Event interface { GetExchangeFee() decimal.Decimal SetExchangeFee(decimal.Decimal) GetOrder() *order.Detail + GetLinkedOrderID() string } diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index 102ba10ad84..47143955057 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -81,3 +81,9 @@ func (o *Order) SetLeverage(l decimal.Decimal) { func (o *Order) GetAllocatedFunds() decimal.Decimal { return o.AllocatedFunds } + +// GetLinkedOrderID returns the order ID of a linked +// futures order +func (o *Order) GetLinkedOrderID() string { + return o.LinkedOrderID +} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index 4f7c6aff3ff..7cf43206404 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -20,6 +20,11 @@ type Order struct { AllocatedFunds decimal.Decimal BuyLimit decimal.Decimal SellLimit decimal.Decimal + // LinkedOrderID is the order ID of the futures order + // to that is being closed. This linking allows the + // more detailed order.Futures struct to close out + // and have more detailed performance tracking + LinkedOrderID string } // Event inherits common event interfaces along with extra functions related to handling orders @@ -36,4 +41,5 @@ type Event interface { GetID() string IsLeveraged() bool GetAllocatedFunds() decimal.Decimal + GetLinkedOrderID() string } diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index 36703086dad..d4a841401e4 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -3,6 +3,7 @@ package signal import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -55,3 +56,27 @@ func (s *Signal) GetPrice() decimal.Decimal { func (s *Signal) SetPrice(f decimal.Decimal) { s.ClosePrice = f } + +// GetAmount retrieves the order amount +func (s *Signal) GetAmount() decimal.Decimal { + return s.Amount +} + +// SetAmount sets the order amount +func (s *Signal) SetAmount(d decimal.Decimal) { + s.Amount = d +} + +// SetCloseOrderID links an existing order id +// for a futures order set to be closed +func (s *Signal) SetCloseOrderID(id string) { + if s.AssetType == asset.Futures { + s.CloseOrderID = id + } +} + +// GetLinkedOrderID returns the order ID of a +// linked futures order +func (s *Signal) GetLinkedOrderID() string { + return s.CloseOrderID +} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index a07cd676f63..06e017d54e3 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -17,6 +17,8 @@ type Event interface { IsSignal() bool GetSellLimit() decimal.Decimal GetBuyLimit() decimal.Decimal + GetAmount() decimal.Decimal + GetLinkedOrderID() string } // Signal contains everything needed for a strategy to raise a signal event @@ -27,7 +29,23 @@ type Signal struct { LowPrice decimal.Decimal ClosePrice decimal.Decimal Volume decimal.Decimal - BuyLimit decimal.Decimal - SellLimit decimal.Decimal - Direction order.Side + // BuyLimit sets a maximum buy from the strategy + // it differs from amount as it is more a suggestion + // use Amount if you wish to have a fillOrKill style amount + BuyLimit decimal.Decimal + // SellLimit sets a maximum sell from the strategy + // it differs from amount as it is more a suggestion + // use Amount if you wish to have a fillOrKill style amount + SellLimit decimal.Decimal + // Amount set the amount when you wish to allow + // a strategy to dictate order quantities + // if the amount is not allowed by the portfolio manager + // the order will not be placed + Amount decimal.Decimal + Direction order.Side + // CloseOrderID is the order ID of the futures order + // to that is being closed. This linking allows the + // more detailed order.Futures struct to close out + // and have more detailed performance tracking + CloseOrderID string } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 9573a3fa669..94dadceb351 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1455,12 +1455,12 @@ {{range $val.FinalOrders.Orders}} - {{ .Detail.Date }} + {{ .SpotOrder.Date }} {{ $.Prettify.Decimal8 .ClosePrice}} {{$pair.Quote}} - {{ .Detail.Side }} - {{$.Prettify.Float8 .Detail.Price }} {{$pair.Quote}} - {{$.Prettify.Float8 .Detail.Amount }} {{$pair.Base}} - {{$.Prettify.Float8 .Detail.Fee }} {{$pair.Quote}} + {{ .SpotOrder.Side }} + {{$.Prettify.Float8 .SpotOrder.Price }} {{$pair.Quote}} + {{$.Prettify.Float8 .SpotOrder.Amount }} {{$pair.Base}} + {{$.Prettify.Float8 .SpotOrder.Fee }} {{$pair.Quote}} {{ $.Prettify.Decimal8 .CostBasis }} {{$pair.Quote}} {{ $.Prettify.Decimal8 .SlippageRate }}% diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 9fad526bfb3..dc5e30e34a0 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -121,14 +121,29 @@ type Futures struct { Side Side UnrealisedPNL decimal.Decimal RealisedPNL decimal.Decimal + UnderlyingAsset currency.Code OpeningPosition *Detail ClosingPosition *Detail PNLHistory []PNLHistory } +// UpsertPNLEntry upserts an entry to PNLHistory field +// with some basic checks +func (f *Futures) UpsertPNLEntry(entry PNLHistory) { + if entry.Time.IsZero() { + return + } + for i := range f.PNLHistory { + if entry.Time.Equal(f.PNLHistory[i].Time) { + f.PNLHistory[i] = entry + return + } + } + f.PNLHistory = append(f.PNLHistory, entry) +} + type PNLHistory struct { - Price decimal.Decimal - Amount decimal.Decimal + Time time.Time UnrealisedPNL decimal.Decimal } From bb0be0b3b761b522fa4f280d495519d4d309c5b6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 5 Nov 2021 16:42:03 +1100 Subject: [PATCH 018/171] Introduces futures concept for collateral and spot/futures config diffs --- backtester/backtest/backtest.go | 60 ++++++++++++--------- backtester/config/config.go | 84 ++++++++++++++++------------- backtester/config/config_types.go | 29 +++++++--- backtester/funding/funding_types.go | 2 + 4 files changed, 103 insertions(+), 72 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 9ab01403601..189e7b51615 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -225,11 +225,14 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, cfg.CurrencySettings[i].Base+cfg.CurrencySettings[i].Quote, err) } - portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = &risk.CurrencySettings{ - MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].Leverage.MaximumOrdersWithLeverageRatio, - MaxLeverageRate: cfg.CurrencySettings[i].Leverage.MaximumLeverageRate, - MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, + portSet := &risk.CurrencySettings{ + MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, } + if cfg.CurrencySettings[i].FuturesDetails != nil { + portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio + portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumLeverageRate + } + portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet if cfg.CurrencySettings[i].MakerFee.GreaterThan(cfg.CurrencySettings[i].TakerFee) { log.Warnf(log.BackTester, "maker fee '%v' should not exceed taker fee '%v'. Please review config", cfg.CurrencySettings[i].MakerFee, @@ -265,11 +268,13 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, } } else { var bFunds, qFunds decimal.Decimal - if cfg.CurrencySettings[i].InitialBaseFunds != nil { - bFunds = *cfg.CurrencySettings[i].InitialBaseFunds - } - if cfg.CurrencySettings[i].InitialQuoteFunds != nil { - qFunds = *cfg.CurrencySettings[i].InitialQuoteFunds + if cfg.CurrencySettings[i].SpotDetails != nil { + if cfg.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { + bFunds = *cfg.CurrencySettings[i].SpotDetails.InitialBaseFunds + } + if cfg.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil { + qFunds = *cfg.CurrencySettings[i].SpotDetails.InitialQuoteFunds + } } baseItem, err = funding.CreateItem( cfg.CurrencySettings[i].ExchangeName, @@ -494,24 +499,27 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange cfg.CurrencySettings[i].ShowExchangeOrderLimitWarning = true } } - + var lev exchange.Leverage + if cfg.CurrencySettings[i].FuturesDetails != nil { + lev = exchange.Leverage{ + CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, + MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumLeverageRate, + MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, + } + } resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ - Exchange: cfg.CurrencySettings[i].ExchangeName, - MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, - MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, - Pair: pair, - Asset: a, - ExchangeFee: takerFee, - MakerFee: takerFee, - TakerFee: makerFee, - UseRealOrders: realOrders, - BuySide: buyRule, - SellSide: sellRule, - Leverage: exchange.Leverage{ - CanUseLeverage: cfg.CurrencySettings[i].Leverage.CanUseLeverage, - MaximumLeverageRate: cfg.CurrencySettings[i].Leverage.MaximumLeverageRate, - MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].Leverage.MaximumOrdersWithLeverageRatio, - }, + Exchange: cfg.CurrencySettings[i].ExchangeName, + MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, + MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, + Pair: pair, + Asset: a, + ExchangeFee: takerFee, + MakerFee: takerFee, + TakerFee: makerFee, + UseRealOrders: realOrders, + BuySide: buyRule, + SellSide: sellRule, + Leverage: lev, Limits: limits, SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, diff --git a/backtester/config/config.go b/backtester/config/config.go index 4236ceff33a..23f6f422b18 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -12,6 +12,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/file" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -75,15 +76,15 @@ func (c *Config) PrintSetting() { log.Infof(log.BackTester, currStr[:61]) log.Info(log.BackTester, "-------------------------------------------------------------") log.Infof(log.BackTester, "Exchange: %v", c.CurrencySettings[i].ExchangeName) - if !c.StrategySettings.UseExchangeLevelFunding { - if c.CurrencySettings[i].InitialBaseFunds != nil { + if !c.StrategySettings.UseExchangeLevelFunding && c.CurrencySettings[i].SpotDetails != nil { + if c.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { log.Infof(log.BackTester, "Initial base funds: %v %v", - c.CurrencySettings[i].InitialBaseFunds.Round(8), + c.CurrencySettings[i].SpotDetails.InitialBaseFunds.Round(8), c.CurrencySettings[i].Base) } - if c.CurrencySettings[i].InitialQuoteFunds != nil { + if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil { log.Infof(log.BackTester, "Initial quote funds: %v %v", - c.CurrencySettings[i].InitialQuoteFunds.Round(8), + c.CurrencySettings[i].SpotDetails.InitialQuoteFunds.Round(8), c.CurrencySettings[i].Quote) } } @@ -93,7 +94,10 @@ func (c *Config) PrintSetting() { log.Infof(log.BackTester, "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) log.Infof(log.BackTester, "Buy rules: %+v", c.CurrencySettings[i].BuySide) log.Infof(log.BackTester, "Sell rules: %+v", c.CurrencySettings[i].SellSide) - log.Infof(log.BackTester, "Leverage rules: %+v", c.CurrencySettings[i].Leverage) + if c.CurrencySettings[i].FuturesDetails != nil && c.CurrencySettings[i].Asset == asset.Futures.String() { + log.Infof(log.BackTester, "Leverage rules: %+v", c.CurrencySettings[i].FuturesDetails.Leverage) + + } log.Infof(log.BackTester, "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) } @@ -269,41 +273,45 @@ func (c *Config) validateCurrencySettings() error { return errNoCurrencySettings } for i := range c.CurrencySettings { - if c.CurrencySettings[i].InitialLegacyFunds > 0 { - // temporarily migrate legacy start config value - log.Warn(log.BackTester, "config field 'initial-funds' no longer supported, please use 'initial-quote-funds'") - log.Warnf(log.BackTester, "temporarily setting 'initial-quote-funds' to 'initial-funds' value of %v", c.CurrencySettings[i].InitialLegacyFunds) - iqf := decimal.NewFromFloat(c.CurrencySettings[i].InitialLegacyFunds) - c.CurrencySettings[i].InitialQuoteFunds = &iqf + if c.CurrencySettings[i].SpotDetails == nil && c.CurrencySettings[i].FuturesDetails == nil { + // woah nelly! } - if c.StrategySettings.UseExchangeLevelFunding { - if c.CurrencySettings[i].InitialQuoteFunds != nil && - c.CurrencySettings[i].InitialQuoteFunds.GreaterThan(decimal.Zero) { - return fmt.Errorf("non-nil quote %w", errBadInitialFunds) - } - if c.CurrencySettings[i].InitialBaseFunds != nil && - c.CurrencySettings[i].InitialBaseFunds.GreaterThan(decimal.Zero) { - return fmt.Errorf("non-nil base %w", errBadInitialFunds) - } - } else { - if c.CurrencySettings[i].InitialQuoteFunds == nil && - c.CurrencySettings[i].InitialBaseFunds == nil { - return fmt.Errorf("nil base and quote %w", errBadInitialFunds) - } - if c.CurrencySettings[i].InitialQuoteFunds != nil && - c.CurrencySettings[i].InitialBaseFunds != nil && - c.CurrencySettings[i].InitialBaseFunds.IsZero() && - c.CurrencySettings[i].InitialQuoteFunds.IsZero() { - return fmt.Errorf("base or quote funds set to zero %w", errBadInitialFunds) - } - if c.CurrencySettings[i].InitialQuoteFunds == nil { - c.CurrencySettings[i].InitialQuoteFunds = &decimal.Zero - } - if c.CurrencySettings[i].InitialBaseFunds == nil { - c.CurrencySettings[i].InitialBaseFunds = &decimal.Zero + if c.CurrencySettings[i].SpotDetails != nil { + // if c.CurrencySettings[i].SpotDetails.InitialLegacyFunds > 0 { + // // temporarily migrate legacy start config value + // log.Warn(log.BackTester, "config field 'initial-funds' no longer supported, please use 'initial-quote-funds'") + // log.Warnf(log.BackTester, "temporarily setting 'initial-quote-funds' to 'initial-funds' value of %v", c.CurrencySettings[i].InitialLegacyFunds) + // iqf := decimal.NewFromFloat(c.CurrencySettings[i].SpotDetails.InitialLegacyFunds) + // c.CurrencySettings[i].SpotDetails.InitialQuoteFunds = &iqf + // } + if c.StrategySettings.UseExchangeLevelFunding { + if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil && + c.CurrencySettings[i].SpotDetails.InitialQuoteFunds.GreaterThan(decimal.Zero) { + return fmt.Errorf("non-nil quote %w", errBadInitialFunds) + } + if c.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil && + c.CurrencySettings[i].SpotDetails.InitialBaseFunds.GreaterThan(decimal.Zero) { + return fmt.Errorf("non-nil base %w", errBadInitialFunds) + } + } else { + if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds == nil && + c.CurrencySettings[i].SpotDetails.InitialBaseFunds == nil { + return fmt.Errorf("nil base and quote %w", errBadInitialFunds) + } + if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil && + c.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil && + c.CurrencySettings[i].SpotDetails.InitialBaseFunds.IsZero() && + c.CurrencySettings[i].SpotDetails.InitialQuoteFunds.IsZero() { + return fmt.Errorf("base or quote funds set to zero %w", errBadInitialFunds) + } + if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds == nil { + c.CurrencySettings[i].SpotDetails.InitialQuoteFunds = &decimal.Zero + } + if c.CurrencySettings[i].SpotDetails.InitialBaseFunds == nil { + c.CurrencySettings[i].SpotDetails.InitialBaseFunds = &decimal.Zero + } } } - if c.CurrencySettings[i].Base == "" { return errUnsetCurrency } diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index c7157a0583e..763bf1c1228 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -120,13 +120,11 @@ type CurrencySettings struct { // USDTrackingPair is used for price tracking data only USDTrackingPair bool `json:"-"` - InitialBaseFunds *decimal.Decimal `json:"initial-base-funds,omitempty"` - InitialQuoteFunds *decimal.Decimal `json:"initial-quote-funds,omitempty"` - InitialLegacyFunds float64 `json:"initial-funds,omitempty"` + SpotDetails *SpotDetails `json:"spot-details,omitempty"` + FuturesDetails *FuturesDetails `json:"futures-details,omitempty"` - Leverage Leverage `json:"leverage"` - BuySide MinMax `json:"buy-side"` - SellSide MinMax `json:"sell-side"` + BuySide MinMax `json:"buy-side"` + SellSide MinMax `json:"sell-side"` MinimumSlippagePercent decimal.Decimal `json:"min-slippage-percent"` MaximumSlippagePercent decimal.Decimal `json:"max-slippage-percent"` @@ -134,13 +132,28 @@ type CurrencySettings struct { MakerFee decimal.Decimal `json:"maker-fee-override"` TakerFee decimal.Decimal `json:"taker-fee-override"` - MaximumHoldingsRatio decimal.Decimal `json:"maximum-holdings-ratio"` + MaximumHoldingsRatio decimal.Decimal `json:"maximum-holdings-ratio"` + SkipCandleVolumeFitting bool `json:"skip-candle-volume-fitting"` CanUseExchangeLimits bool `json:"use-exchange-order-limits"` - SkipCandleVolumeFitting bool `json:"skip-candle-volume-fitting"` ShowExchangeOrderLimitWarning bool `json:"-"` } +type SpotDetails struct { + InitialBaseFunds *decimal.Decimal `json:"initial-base-funds,omitempty"` + InitialQuoteFunds *decimal.Decimal `json:"initial-quote-funds,omitempty"` +} + +type FuturesDetails struct { + Leverage Leverage `json:"leverage"` + // CollateralCurrency if the asset is futures, then this field + // allows a user to nominate another currency to be used as collateral + // while trading futures contracts + // This currency is a reference to a funding item setup in + // strategy + CollateralCurrency string `json:"collateral-currency"` +} + // APIData defines all fields to configure API based data type APIData struct { StartDate time.Time `json:"start-date"` diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 0b54d2b44bc..55b265dfdaf 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -77,6 +77,8 @@ type Item struct { pairedWith *Item usdTrackingCandles *kline.DataFromKline snapshot map[time.Time]ItemSnapshot + collateral bool + collateralCandles map[currency.Code]kline.DataFromKline } // Pair holds two currencies that are associated with each other From 5b653cfc7eef057641ac14e30428584a8f995af6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 8 Nov 2021 15:24:22 +1100 Subject: [PATCH 019/171] Fixes most tests --- backtester/backtest/backtest_test.go | 84 +++--- backtester/config/config.go | 2 +- backtester/config/config_test.go | 247 ++++++++---------- backtester/config/configbuilder/main.go | 50 ++-- .../eventhandlers/portfolio/risk/risk_test.go | 2 +- .../statistics/currencystatistics_test.go | 16 +- .../strategies/futures/futures.go | 2 +- .../strategies/futures/futures_test.go | 6 +- backtester/report/report_test.go | 6 +- 9 files changed, 207 insertions(+), 208 deletions(-) diff --git a/backtester/backtest/backtest_test.go b/backtester/backtest/backtest_test.go index 082cb70e785..e792da624be 100644 --- a/backtester/backtest/backtest_test.go +++ b/backtester/backtest/backtest_test.go @@ -134,16 +134,17 @@ func TestLoadDataAPI(t *testing.T) { cfg := &config.Config{ CurrencySettings: []config.CurrencySettings{ { - ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), - InitialQuoteFunds: leet, - Leverage: config.Leverage{}, - BuySide: config.MinMax{}, - SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + ExchangeName: "Binance", + Asset: asset.Spot.String(), + Base: cp.Base.String(), + Quote: cp.Quote.String(), + SpotDetails: &config.SpotDetails{ + InitialQuoteFunds: leet, + }, + BuySide: config.MinMax{}, + SellSide: config.MinMax{}, + MakerFee: decimal.Zero, + TakerFee: decimal.Zero, }, }, DataSettings: config.DataSettings{ @@ -190,16 +191,17 @@ func TestLoadDataDatabase(t *testing.T) { cfg := &config.Config{ CurrencySettings: []config.CurrencySettings{ { - ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), - InitialQuoteFunds: leet, - Leverage: config.Leverage{}, - BuySide: config.MinMax{}, - SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + ExchangeName: "Binance", + Asset: asset.Spot.String(), + Base: cp.Base.String(), + Quote: cp.Quote.String(), + SpotDetails: &config.SpotDetails{ + InitialQuoteFunds: leet, + }, + BuySide: config.MinMax{}, + SellSide: config.MinMax{}, + MakerFee: decimal.Zero, + TakerFee: decimal.Zero, }, }, DataSettings: config.DataSettings{ @@ -257,16 +259,17 @@ func TestLoadDataCSV(t *testing.T) { cfg := &config.Config{ CurrencySettings: []config.CurrencySettings{ { - ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), - InitialQuoteFunds: leet, - Leverage: config.Leverage{}, - BuySide: config.MinMax{}, - SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + ExchangeName: "Binance", + Asset: asset.Spot.String(), + Base: cp.Base.String(), + Quote: cp.Quote.String(), + SpotDetails: &config.SpotDetails{ + InitialQuoteFunds: leet, + }, + BuySide: config.MinMax{}, + SellSide: config.MinMax{}, + MakerFee: decimal.Zero, + TakerFee: decimal.Zero, }, }, DataSettings: config.DataSettings{ @@ -314,16 +317,17 @@ func TestLoadDataLive(t *testing.T) { cfg := &config.Config{ CurrencySettings: []config.CurrencySettings{ { - ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), - InitialQuoteFunds: leet, - Leverage: config.Leverage{}, - BuySide: config.MinMax{}, - SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + ExchangeName: "Binance", + Asset: asset.Spot.String(), + Base: cp.Base.String(), + Quote: cp.Quote.String(), + SpotDetails: &config.SpotDetails{ + InitialQuoteFunds: leet, + }, + BuySide: config.MinMax{}, + SellSide: config.MinMax{}, + MakerFee: decimal.Zero, + TakerFee: decimal.Zero, }, }, DataSettings: config.DataSettings{ diff --git a/backtester/config/config.go b/backtester/config/config.go index 23f6f422b18..57c9d2434ed 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -274,7 +274,7 @@ func (c *Config) validateCurrencySettings() error { } for i := range c.CurrencySettings { if c.CurrencySettings[i].SpotDetails == nil && c.CurrencySettings[i].FuturesDetails == nil { - // woah nelly! + return fmt.Errorf("%w please add spot or future currency details", errNoCurrencySettings) } if c.CurrencySettings[i].SpotDetails != nil { // if c.CurrencySettings[i].SpotDetails.InitialLegacyFunds > 0 { diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 941aeca1acf..a72c05e1320 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -104,16 +104,15 @@ func TestPrintSettings(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds1, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds1, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -167,16 +166,15 @@ func TestGenerateConfigForDCAAPICandles(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -243,7 +241,6 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -254,7 +251,6 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -304,16 +300,15 @@ func TestGenerateConfigForDCAAPITrades(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: "ftx", - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: "ftx", + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, SkipCandleVolumeFitting: true, @@ -372,30 +367,28 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.ETH.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -446,30 +439,28 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds1, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds1, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.ETH.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -520,16 +511,15 @@ func TestGenerateConfigForDCALiveCandles(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -587,31 +577,29 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), - InitialBaseFunds: initialBaseFunds, - InitialQuoteFunds: initialQuoteFunds1, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.ETH.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialBaseFunds: initialBaseFunds, + InitialQuoteFunds: initialQuoteFunds1, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -663,16 +651,15 @@ func TestGenerateConfigForDCACSVCandles(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -722,13 +709,12 @@ func TestGenerateConfigForDCACSVTrades(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, MakerFee: makerFee, TakerFee: takerFee, @@ -775,16 +761,15 @@ func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialQuoteFunds: initialQuoteFunds2, - BuySide: minMax, - SellSide: minMax, - Leverage: Leverage{ - CanUseLeverage: false, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, }, + BuySide: minMax, + SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -870,7 +855,6 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -881,7 +865,6 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -892,7 +875,6 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.BTC.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -903,7 +885,6 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.BTC.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -914,7 +895,6 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -925,7 +905,6 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.BTC.String(), BuySide: minMax, SellSide: minMax, - Leverage: Leverage{}, MakerFee: makerFee, TakerFee: takerFee, }, @@ -1013,11 +992,11 @@ func TestValidateCurrencySettings(t *testing.T) { } c.CurrencySettings = append(c.CurrencySettings, CurrencySettings{}) err = c.validateCurrencySettings() - if !errors.Is(err, errBadInitialFunds) { - t.Errorf("received: %v, expected: %v", err, errBadInitialFunds) + if !errors.Is(err, errNoCurrencySettings) { + t.Errorf("received: %v, expected: %v", err, errNoCurrencySettings) } leet := decimal.NewFromInt(1337) - c.CurrencySettings[0].InitialQuoteFunds = &leet + c.CurrencySettings[0].SpotDetails = &SpotDetails{InitialQuoteFunds: &leet} err = c.validateCurrencySettings() if !errors.Is(err, errUnsetCurrency) { t.Errorf("received: %v, expected: %v", err, errUnsetCurrency) @@ -1198,12 +1177,14 @@ func TestValidate(t *testing.T) { StrategySettings: StrategySettings{Name: dca}, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - InitialBaseFunds: initialBaseFunds, - InitialQuoteFunds: initialQuoteFunds2, + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Base: currency.BTC.String(), + Quote: currency.USDT.String(), + SpotDetails: &SpotDetails{ + InitialBaseFunds: initialBaseFunds, + InitialQuoteFunds: initialQuoteFunds2, + }, BuySide: MinMax{ MinimumSize: decimal.NewFromInt(1), MaximumSize: decimal.NewFromInt(10), diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 10016732cd6..3f0dde9c980 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -584,32 +584,46 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* var f float64 fmt.Println("Enter the currency base. eg BTC") setting.Base = quickParse(reader) - if !usingExchangeLevelFunding { - fmt.Println("Enter the initial base funds. eg 0") - parseNum := quickParse(reader) - if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) - if err != nil { - return nil, err + switch setting.Asset { + case asset.Spot.String(): + setting.SpotDetails = &config.SpotDetails{} + if !usingExchangeLevelFunding { + fmt.Println("Enter the initial base funds. eg 0") + parseNum := quickParse(reader) + if parseNum != "" { + f, err = strconv.ParseFloat(parseNum, 64) + if err != nil { + return nil, err + } + iqf := decimal.NewFromFloat(f) + setting.SpotDetails.InitialBaseFunds = &iqf } - iqf := decimal.NewFromFloat(f) - setting.InitialBaseFunds = &iqf } + case asset.Futures.String(): + } + fmt.Println("Enter the currency quote. eg USDT") setting.Quote = quickParse(reader) - if !usingExchangeLevelFunding { - fmt.Println("Enter the initial quote funds. eg 10000") - parseNum := quickParse(reader) - if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) - if err != nil { - return nil, err + + switch setting.Asset { + case asset.Spot.String(): + if !usingExchangeLevelFunding { + fmt.Println("Enter the initial quote funds. eg 10000") + parseNum := quickParse(reader) + if parseNum != "" { + f, err = strconv.ParseFloat(parseNum, 64) + if err != nil { + return nil, err + } + iqf := decimal.NewFromFloat(f) + setting.SpotDetails.InitialQuoteFunds = &iqf } - iqf := decimal.NewFromFloat(f) - setting.InitialQuoteFunds = &iqf } + case asset.Futures.String(): + } + fmt.Println("Enter the maker-fee. eg 0.001") parseNum := quickParse(reader) if parseNum != "" { diff --git a/backtester/eventhandlers/portfolio/risk/risk_test.go b/backtester/eventhandlers/portfolio/risk/risk_test.go index 92c585dcc9c..553749085be 100644 --- a/backtester/eventhandlers/portfolio/risk/risk_test.go +++ b/backtester/eventhandlers/portfolio/risk/risk_test.go @@ -117,7 +117,7 @@ func TestEvaluateOrder(t *testing.T) { _, err = r.EvaluateOrder(o, h, compliance.Snapshot{ Orders: []compliance.SnapshotOrder{ { - Detail: &gctorder.Detail{ + SpotOrder: &gctorder.Detail{ Leverage: 3, }, }, diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index a04213b5322..aa54961fca9 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -45,14 +45,14 @@ func TestCalculateResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - Detail: &order.Detail{Side: order.Buy}, + SpotOrder: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - Detail: &order.Detail{Side: order.Sell}, + SpotOrder: &order.Detail{Side: order.Sell}, }, }, }, @@ -84,14 +84,14 @@ func TestCalculateResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - Detail: &order.Detail{Side: order.Buy}, + SpotOrder: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - Detail: &order.Detail{Side: order.Sell}, + SpotOrder: &order.Detail{Side: order.Sell}, }, }, }, @@ -182,14 +182,14 @@ func TestPrintResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - Detail: &order.Detail{Side: order.Buy}, + SpotOrder: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - Detail: &order.Detail{Side: order.Sell}, + SpotOrder: &order.Detail{Side: order.Sell}, }, }, }, @@ -221,14 +221,14 @@ func TestPrintResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - Detail: &order.Detail{Side: order.Buy}, + SpotOrder: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - Detail: &order.Detail{Side: order.Sell}, + SpotOrder: &order.Detail{Side: order.Sell}, }, }, }, diff --git a/backtester/eventhandlers/strategies/futures/futures.go b/backtester/eventhandlers/strategies/futures/futures.go index fbb6e7194be..02cb6ac8676 100644 --- a/backtester/eventhandlers/strategies/futures/futures.go +++ b/backtester/eventhandlers/strategies/futures/futures.go @@ -1,4 +1,4 @@ -package rsi +package futures import ( "errors" diff --git a/backtester/eventhandlers/strategies/futures/futures_test.go b/backtester/eventhandlers/strategies/futures/futures_test.go index 377183d1ef8..d4585a61ff0 100644 --- a/backtester/eventhandlers/strategies/futures/futures_test.go +++ b/backtester/eventhandlers/strategies/futures/futures_test.go @@ -1,4 +1,4 @@ -package rsi +package futures import ( "errors" @@ -30,8 +30,8 @@ func TestName(t *testing.T) { func TestSupportsSimultaneousProcessing(t *testing.T) { t.Parallel() s := Strategy{} - if !s.SupportsSimultaneousProcessing() { - t.Error("expected true") + if s.SupportsSimultaneousProcessing() { + t.Error("expected false") } } diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index 998a08ea3d2..145b342fcab 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -410,7 +410,7 @@ func TestEnhanceCandles(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1), CostBasis: decimal.NewFromInt(1337), - Detail: nil, + SpotOrder: nil, }, }, Timestamp: tt, @@ -427,7 +427,7 @@ func TestEnhanceCandles(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1), CostBasis: decimal.NewFromInt(1337), - Detail: &gctorder.Detail{ + SpotOrder: &gctorder.Detail{ Date: tt, Side: gctorder.Buy, }, @@ -447,7 +447,7 @@ func TestEnhanceCandles(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1), CostBasis: decimal.NewFromInt(1337), - Detail: &gctorder.Detail{ + SpotOrder: &gctorder.Detail{ Date: tt, Side: gctorder.Sell, }, From 4fbc6f888dbb5c3148b67ff616f4ac46cca950e9 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 8 Nov 2021 16:57:51 +1100 Subject: [PATCH 020/171] Simple designs for collateral funding pair concept --- backtester/common/common_types.go | 4 +- .../eventhandlers/portfolio/portfolio.go | 85 +++++++++++-------- .../portfolio/portfolio_types.go | 2 + backtester/funding/funding.go | 16 +++- backtester/funding/funding_types.go | 19 ++++- 5 files changed, 89 insertions(+), 37 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 4fabbb331b5..bce63c75562 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -22,7 +22,9 @@ const ( CouldNotBuy order.Side = "COULD NOT BUY" // CouldNotSell is flagged when a SELL signal is raised in the strategy/signal phase, but the // portfolio manager or exchange cannot place an order - CouldNotSell order.Side = "COULD NOT SELL" + CouldNotSell order.Side = "COULD NOT SELL" + CouldNotShort order.Side = "COULD NOT SHORT" + CouldNotLong order.Side = "COULD NOT LONG" // MissingData is signalled during the strategy/signal phase when data has been identified as missing // No buy or sell events can occur MissingData order.Side = "MISSING DATA" diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 604ffa96f45..6651f1b6085 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -46,31 +46,6 @@ func (p *Portfolio) Reset() { p.exchangeAssetPairSettings = nil } -// GetLatestOrderSnapshotForEvent gets orders related to the event -func (p *Portfolio) GetLatestOrderSnapshotForEvent(e common.EventHandler) (compliance.Snapshot, error) { - eapSettings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] - if !ok { - return compliance.Snapshot{}, fmt.Errorf("%w for %v %v %v", errNoPortfolioSettings, e.GetExchange(), e.GetAssetType(), e.Pair()) - } - return eapSettings.ComplianceManager.GetLatestSnapshot(), nil -} - -// GetLatestOrderSnapshots returns the latest snapshots from all stored pair data -func (p *Portfolio) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) { - var resp []compliance.Snapshot - for _, exchangeMap := range p.exchangeAssetPairSettings { - for _, assetMap := range exchangeMap { - for _, pairMap := range assetMap { - resp = append(resp, pairMap.ComplianceManager.GetLatestSnapshot()) - } - } - } - if len(resp) == 0 { - return nil, errNoPortfolioSettings - } - return resp, nil -} - // OnSignal receives the event from the strategy on whether it has signalled to buy, do nothing or sell // on buy/sell, the portfolio manager will size the order and assess the risk of the order // if successful, it will pass on an order.Order to be used by the exchange event handler to place an order based on @@ -122,13 +97,18 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return o, nil } - if !funds.CanPlaceOrder(ev.GetDirection()) { - if ev.GetDirection() == gctorder.Sell { - o.AppendReason("no holdings to sell") + dir := ev.GetDirection() + if !funds.CanPlaceOrder(dir) { + o.AppendReason(notEnoughFundsTo + " " + dir.Lower()) + switch ev.GetDirection() { + case gctorder.Sell: o.SetDirection(common.CouldNotSell) - } else if ev.GetDirection() == gctorder.Buy { - o.AppendReason("not enough funds to buy") + case gctorder.Buy: o.SetDirection(common.CouldNotBuy) + case gctorder.Short: + o.SetDirection(common.CouldNotShort) + case gctorder.Long: + o.SetDirection(common.CouldNotLong) } ev.SetDirection(o.Direction) return o, nil @@ -139,10 +119,22 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi o.BuyLimit = ev.GetBuyLimit() o.SellLimit = ev.GetSellLimit() var sizingFunds decimal.Decimal - if ev.GetDirection() == gctorder.Sell { - sizingFunds = funds.BaseAvailable() - } else { - sizingFunds = funds.QuoteAvailable() + if ev.GetAssetType() == asset.Spot { + pReader, err := funds.GetPairReader() + if err != nil { + return nil, err + } + if ev.GetDirection() == gctorder.Sell { + sizingFunds = pReader.BaseAvailable() + } else { + sizingFunds = pReader.QuoteAvailable() + } + } else if ev.GetAssetType() == asset.Futures { + cReader, err := funds.GetCollateralReader() + if err != nil { + return nil, err + } + sizingFunds = cReader.AvailableFunds() } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) @@ -277,6 +269,31 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IPairReader) (*fill.Fi return fe, nil } +// GetLatestOrderSnapshotForEvent gets orders related to the event +func (p *Portfolio) GetLatestOrderSnapshotForEvent(e common.EventHandler) (compliance.Snapshot, error) { + eapSettings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] + if !ok { + return compliance.Snapshot{}, fmt.Errorf("%w for %v %v %v", errNoPortfolioSettings, e.GetExchange(), e.GetAssetType(), e.Pair()) + } + return eapSettings.ComplianceManager.GetLatestSnapshot(), nil +} + +// GetLatestOrderSnapshots returns the latest snapshots from all stored pair data +func (p *Portfolio) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) { + var resp []compliance.Snapshot + for _, exchangeMap := range p.exchangeAssetPairSettings { + for _, assetMap := range exchangeMap { + for _, pairMap := range assetMap { + resp = append(resp, pairMap.ComplianceManager.GetLatestSnapshot()) + } + } + } + if len(resp) == 0 { + return nil, errNoPortfolioSettings + } + return resp, nil +} + // addComplianceSnapshot gets the previous snapshot of compliance events, updates with the latest fillevent // then saves the snapshot to the c func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index b2bcd6af17d..845ddb5b622 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -17,6 +17,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) +const notEnoughFundsTo = "not enough funds to" + var ( errInvalidDirection = errors.New("invalid direction") errRiskManagerUnset = errors.New("risk manager unset") diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 09827411234..2927fe38fa6 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -23,7 +23,9 @@ var ( // ErrAlreadyExists used when a matching item or pair is already in the funding manager ErrAlreadyExists = errors.New("funding already exists") // ErrUSDTrackingDisabled used when attempting to track USD values when disabled - ErrUSDTrackingDisabled = errors.New("USD tracking disabled") + ErrUSDTrackingDisabled = errors.New("USD tracking disabled") + // ErrNotCollateral is returned when a user requests collateral from a non-collateral pair + ErrNotCollateral = errors.New("not a collateral pair") errCannotAllocate = errors.New("cannot allocate funds") errZeroAmountReceived = errors.New("amount received less than or equal to zero") errNegativeAmountReceived = errors.New("received negative decimal") @@ -388,6 +390,14 @@ func (p *Pair) QuoteAvailable() decimal.Decimal { return p.Quote.available } +func (p *Pair) GetPairReader() (IPairReader, error) { + return p, nil +} + +func (p *Pair) GetCollateralReader() (ICollateralReader, error) { + return nil, ErrNotCollateral +} + // Reserve allocates an amount of funds to be used at a later time // it prevents multiple events from claiming the same resource // changes which currency to affect based on the order side @@ -450,6 +460,10 @@ func (p *Pair) CanPlaceOrder(side order.Side) bool { return false } +func (c *CollateralPair) CanPlaceOrder(_ order.Side) bool { + return c.Collateral.CanPlaceOrder() +} + // Reserve allocates an amount of funds to be used at a later time // it prevents multiple events from claiming the same resource func (i *Item) Reserve(amount decimal.Decimal) error { diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 55b265dfdaf..ca023119ffa 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -52,11 +52,18 @@ type IPairReader interface { QuoteAvailable() decimal.Decimal } +type ICollateralReader interface { + Currency() currency.Code + InitialFunds() decimal.Decimal + AvailableFunds() decimal.Decimal +} + // IPairReserver limits funding usage for portfolio event handling type IPairReserver interface { - IPairReader CanPlaceOrder(order.Side) bool Reserve(decimal.Decimal, order.Side) error + GetPairReader() (IPairReader, error) + GetCollateralReader() (ICollateralReader, error) } // IPairReleaser limits funding usage for exchange event handling @@ -65,6 +72,9 @@ type IPairReleaser interface { Release(decimal.Decimal, decimal.Decimal, order.Side) error } +type ICollateralReleaser interface { +} + // Item holds funding data per currency item type Item struct { exchange string @@ -87,6 +97,13 @@ type Pair struct { Quote *Item } +// CollateralPair consists of a currency pair for a futures contract +// and associates it with an addition collateral pair to take funding from +type CollateralPair struct { + Contract *Pair + Collateral *Item +} + // Report holds all funding data for result reporting type Report struct { DisableUSDTracking bool From a6a6a47b7a3b8eea6defaa45cbeb2584e0a0078a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 9 Nov 2021 17:08:39 +1100 Subject: [PATCH 021/171] Expands interface use so much it hurts --- backtester/backtest/backtest.go | 91 ++++--- backtester/common/common_types.go | 3 + backtester/eventhandlers/exchange/exchange.go | 72 ++++-- .../eventhandlers/exchange/exchange_types.go | 2 +- .../eventhandlers/portfolio/portfolio.go | 80 +++--- .../portfolio/portfolio_types.go | 6 +- .../strategies/top2bottom2/top2bottom2.go | 4 +- backtester/funding/collateral.go | 50 ++++ backtester/funding/funding.go | 234 ++---------------- backtester/funding/funding_types.go | 61 +++-- backtester/funding/item.go | 116 +++++++++ backtester/funding/pair.go | 133 ++++++++++ exchanges/order/order_types.go | 15 +- 13 files changed, 539 insertions(+), 328 deletions(-) create mode 100644 backtester/funding/collateral.go create mode 100644 backtester/funding/item.go create mode 100644 backtester/funding/pair.go diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 189e7b51615..d1cf4dbaae7 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -239,32 +239,52 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, cfg.CurrencySettings[i].TakerFee) } - var baseItem, quoteItem *funding.Item + var baseItem, quoteItem, futureItem *funding.Item if cfg.StrategySettings.UseExchangeLevelFunding { - // add any remaining currency items that have no funding data in the strategy config - baseItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, - a, - b, - decimal.Zero, - decimal.Zero) - if err != nil { - return nil, err - } - quoteItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, - a, - q, - decimal.Zero, - decimal.Zero) - if err != nil { - return nil, err - } - err = funds.AddItem(baseItem) - if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { - return nil, err - } - err = funds.AddItem(quoteItem) - if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { - return nil, err + switch a { + case asset.Spot: + // add any remaining currency items that have no funding data in the strategy config + baseItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, + a, + b, + decimal.Zero, + decimal.Zero) + if err != nil { + return nil, err + } + quoteItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, + a, + q, + decimal.Zero, + decimal.Zero) + if err != nil { + return nil, err + } + err = funds.AddItem(baseItem) + if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { + return nil, err + } + err = funds.AddItem(quoteItem) + if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { + return nil, err + } + case asset.Futures: + // setup contract items + c := funding.CreateFuturesCurrencyCode(b, q) + futureItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, + a, + c, + decimal.Zero, + decimal.Zero) + if err != nil { + return nil, err + } + err = funds.AddItem(futureItem) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("%w %v unsupported", errInvalidConfigAsset, a) } } else { var bFunds, qFunds decimal.Decimal @@ -883,6 +903,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { if err != nil { return err } + switch eType := ev.(type) { case common.DataEventHandler: if bt.Strategy.UsingSimultaneousProcessing() { @@ -893,18 +914,18 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { bt.Funding.CreateSnapshot(ev.GetTime()) return nil } - err = bt.processSingleDataEvent(eType, funds) + err = bt.processSingleDataEvent(eType, funds.FundReleaser()) if err != nil { return err } bt.Funding.CreateSnapshot(ev.GetTime()) return nil case signal.Event: - bt.processSignalEvent(eType, funds) + bt.processSignalEvent(eType, funds.FundReserver()) case order.Event: - bt.processOrderEvent(eType, funds) + bt.processOrderEvent(eType, funds.FundReleaser()) case fill.Event: - bt.processFillEvent(eType, funds) + bt.processFillEvent(eType, funds.FundReleaser()) default: return fmt.Errorf("%w %v received, could not process", errUnhandledDatatype, @@ -914,7 +935,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { return nil } -func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds funding.IPairReader) error { +func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds funding.IFundReleaser) error { err := bt.updateStatsForDataEvent(ev, funds) if err != nil { return err @@ -957,7 +978,7 @@ func (bt *BackTest) processSimultaneousDataEvents() error { if err != nil { return err } - err = bt.updateStatsForDataEvent(latestData, funds) + err = bt.updateStatsForDataEvent(latestData, funds.FundReleaser()) if err != nil && err == statistics.ErrAlreadyProcessed { continue } @@ -986,7 +1007,7 @@ func (bt *BackTest) processSimultaneousDataEvents() error { // updateStatsForDataEvent makes various systems aware of price movements from // data events -func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds funding.IPairReader) error { +func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds funding.IFundReleaser) error { // update statistics with the latest price err := bt.Statistic.SetupEventForTime(ev) if err != nil { @@ -1009,7 +1030,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } // processSignalEvent receives an event from the strategy for processing under the portfolio -func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IPairReserver) { +func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) { cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Error(log.BackTester, err) @@ -1029,7 +1050,7 @@ func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IPairReser bt.EventQueue.AppendEvent(o) } -func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IPairReleaser) { +func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundReleaser) { d := bt.Datas.GetDataForCurrency(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) f, err := bt.Exchange.ExecuteOrder(ev, d, bt.orderManager, funds) if err != nil { @@ -1046,7 +1067,7 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IPairRelease bt.EventQueue.AppendEvent(f) } -func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IPairReader) { +func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) { t, err := bt.Portfolio.OnFill(ev, funds) if err != nil { log.Error(log.BackTester, err) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index bce63c75562..97766cab0ef 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -25,6 +25,9 @@ const ( CouldNotSell order.Side = "COULD NOT SELL" CouldNotShort order.Side = "COULD NOT SHORT" CouldNotLong order.Side = "COULD NOT LONG" + + CouldNotCloseShort order.Side = "COULD NOT CLOSE SHORT" + CouldNotCloseLong order.Side = "COULD NOT CLOSE LONG" // MissingData is signalled during the strategy/signal phase when data has been identified as missing // No buy or sell events can occur MissingData order.Side = "MISSING DATA" diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 2ae8f1881ff..862f0fff580 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -27,7 +27,7 @@ func (e *Exchange) Reset() { // ExecuteOrder assesses the portfolio manager's order event and if it passes validation // will send an order to the exchange/fake order manager to be stored and raise a fill event -func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager *engine.OrderManager, funds funding.IPairReleaser) (*fill.Fill, error) { +func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager *engine.OrderManager, funds funding.IFundReleaser) (*fill.Fill, error) { f := &fill.Fill{ Base: event.Base{ Offset: o.GetOffset(), @@ -112,31 +112,67 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.ExchangeFee = calculateExchangeFee(adjustedPrice, limitReducedAmount, cs.ExchangeFee) orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) - if err != nil { - fundErr := funds.Release(eventFunds, eventFunds, f.GetDirection()) + switch cs.Asset { + case asset.Spot: + pr, fundErr := funds.GetPairReleaser() if fundErr != nil { - f.AppendReason(fundErr.Error()) - } - if f.GetDirection() == gctorder.Buy { - f.SetDirection(common.CouldNotBuy) - } else if f.GetDirection() == gctorder.Sell { - f.SetDirection(common.CouldNotSell) + return f, err } - return f, err - } - switch f.GetDirection() { - case gctorder.Buy: - err = funds.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) if err != nil { + fundErr = pr.Release(eventFunds, eventFunds, f.GetDirection()) + if fundErr != nil { + f.AppendReason(fundErr.Error()) + } + if f.GetDirection() == gctorder.Buy { + f.SetDirection(common.CouldNotBuy) + } else if f.GetDirection() == gctorder.Sell { + f.SetDirection(common.CouldNotSell) + } return f, err } - funds.IncreaseAvailable(limitReducedAmount, f.GetDirection()) - case gctorder.Sell: - err = funds.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) + switch f.GetDirection() { + case gctorder.Buy: + fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) + if fundErr != nil { + return f, err + } + pr.IncreaseAvailable(limitReducedAmount, f.GetDirection()) + case gctorder.Sell: + fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) + if fundErr != nil { + return f, fundErr + } + pr.IncreaseAvailable(limitReducedAmount.Mul(adjustedPrice), f.GetDirection()) + } + case asset.Futures: + cr, fundErr := funds.GetCollateralReleaser() + if fundErr != nil { + return f, fundErr + } if err != nil { + fundErr = cr.ReleaseContracts(o.GetAmount()) + if fundErr != nil { + return f, fundErr + } + if o.GetLinkedOrderID() != "" { + switch f.GetDirection() { + case gctorder.Short: + f.SetDirection(common.CouldNotCloseLong) + case gctorder.Long: + f.SetDirection(common.CouldNotCloseShort) + } + } else { + switch f.GetDirection() { + case gctorder.Short: + f.SetDirection(common.CouldNotShort) + case gctorder.Long: + f.SetDirection(common.CouldNotLong) + } + } return f, err } - funds.IncreaseAvailable(limitReducedAmount.Mul(adjustedPrice), f.GetDirection()) + // realising pnl for a closed futures order occurs in the + // portfolio OnFill function } ords := orderManager.GetOrdersSnapshot("") diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index 382963b7a2b..8b12cca7c51 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -25,7 +25,7 @@ var ( type ExecutionHandler interface { SetExchangeAssetCurrencySettings(string, asset.Item, currency.Pair, *Settings) GetCurrencySettings(string, asset.Item, currency.Pair) (Settings, error) - ExecuteOrder(order.Event, data.Handler, *engine.OrderManager, funding.IPairReleaser) (*fill.Fill, error) + ExecuteOrder(order.Event, data.Handler, *engine.OrderManager, funding.IFundReleaser) (*fill.Fill, error) Reset() } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 6651f1b6085..4b9a2a0d320 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -50,7 +50,7 @@ func (p *Portfolio) Reset() { // on buy/sell, the portfolio manager will size the order and assess the risk of the order // if successful, it will pass on an order.Order to be used by the exchange event handler to place an order based on // the portfolio manager's recommendations -func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds funding.IPairReserver) (*order.Order, error) { +func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds funding.IFundReserver) (*order.Order, error) { if ev == nil || cs == nil { return nil, common.ErrNilArguments } @@ -167,7 +167,7 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, siz return evaluatedOrder, nil } -func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, originalOrderSignal *order.Order, sizingFunds decimal.Decimal, funds funding.IPairReserver) *order.Order { +func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, originalOrderSignal *order.Order, sizingFunds decimal.Decimal, funds funding.IFundReserver) *order.Order { sizedOrder, err := p.sizeManager.SizeOrder(originalOrderSignal, sizingFunds, cs) if err != nil { originalOrderSignal.AppendReason(err.Error()) @@ -210,7 +210,7 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi } // OnFill processes the event after an order has been placed by the exchange. Its purpose is to track holdings for future portfolio decisions. -func (p *Portfolio) OnFill(ev fill.Event, funding funding.IPairReader) (*fill.Fill, error) { +func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill.Fill, error) { if ev == nil { return nil, common.ErrNilEvent } @@ -220,19 +220,36 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IPairReader) (*fill.Fi } var err error + if ev.GetLinkedOrderID() != "" { + // this means we're closing an order + snap := lookup.ComplianceManager.GetLatestSnapshot() + for i := range snap.Orders { + if ev.GetLinkedOrderID() == snap.Orders[i].FuturesOrder.OpeningPosition.ID { + snap.Orders[i].FuturesOrder.ClosingPosition = ev.GetOrder() + snap.Orders[i].FuturesOrder.RealisedPNL = snap.Orders[i].FuturesOrder.UnrealisedPNL + + } + } + } + + fp, err := funding.GetPairReleaser() + if err != nil { + return nil, err + } + // Get the holding from the previous iteration, create it if it doesn't yet have a timestamp h := lookup.GetHoldingsForTime(ev.GetTime().Add(-ev.GetInterval().Duration())) if !h.Timestamp.IsZero() { - h.Update(ev, funding) + h.Update(ev, fp) } else { h = lookup.GetLatestHoldings() if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, funding) + h, err = holdings.Create(ev, fp) if err != nil { return nil, err } } else { - h.Update(ev, funding) + h.Update(ev, fp) } } err = p.setHoldingsForOffset(&h, true) @@ -247,25 +264,25 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IPairReader) (*fill.Fi if err != nil { log.Error(log.BackTester, err) } + fe, ok := ev.(*fill.Fill) + if !ok { + return nil, fmt.Errorf("%w expected fill event", common.ErrInvalidDataType) + } direction := ev.GetDirection() if direction == common.DoNothing || direction == common.CouldNotBuy || direction == common.CouldNotSell || direction == common.MissingData || + direction == common.CouldNotCloseLong || + direction == common.CouldNotCloseShort || + direction == common.CouldNotLong || + direction == common.CouldNotShort || direction == "" { - fe, ok := ev.(*fill.Fill) - if !ok { - return nil, fmt.Errorf("%w expected fill event", common.ErrInvalidDataType) - } fe.ExchangeFee = decimal.Zero return fe, nil } - fe, ok := ev.(*fill.Fill) - if !ok { - return nil, fmt.Errorf("%w expected fill event", common.ErrInvalidDataType) - } return fe, nil } @@ -368,7 +385,7 @@ func (p *Portfolio) GetFee(exchangeName string, a asset.Item, cp currency.Pair) } // UpdateHoldings updates the portfolio holdings for the data event -func (p *Portfolio) UpdateHoldings(ev common.DataEventHandler, funds funding.IPairReader) error { +func (p *Portfolio) UpdateHoldings(ev common.DataEventHandler, funds funding.IFundReleaser) error { if ev == nil { return common.ErrNilEvent } @@ -384,9 +401,12 @@ func (p *Portfolio) UpdateHoldings(ev common.DataEventHandler, funds funding.IPa ev.Pair()) } h := lookup.GetLatestHoldings() - if h.Timestamp.IsZero() { - var err error - h, err = holdings.Create(ev, funds) + if h.Timestamp.IsZero() && ev.GetAssetType() == asset.Spot { + f, err := funds.GetPairReader() + if err != nil { + return err + } + h, err = holdings.Create(ev, f) if err != nil { return err } @@ -518,29 +538,29 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // CalculatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { - orders, err := p.GetLatestOrderSnapshotForEvent(e) + snapshot, err := p.GetLatestOrderSnapshotForEvent(e) if err != nil { return err } - for i := range orders.Orders { - if orders.Orders[i].FuturesOrder == nil { + for i := range snapshot.Orders { + if snapshot.Orders[i].FuturesOrder == nil { continue } - if orders.Orders[i].FuturesOrder.ClosingPosition != nil { + if snapshot.Orders[i].FuturesOrder.ClosingPosition != nil { continue } - if orders.Orders[i].FuturesOrder.OpeningPosition.Leverage == 0 { - orders.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 + if snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage == 0 { + snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 } - leverage := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Leverage) - openPrice := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Price) - openAmount := decimal.NewFromFloat(orders.Orders[i].FuturesOrder.OpeningPosition.Amount) + leverage := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage) + openPrice := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Price) + openAmount := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount) changeInPosition := e.ClosePrice().Sub(openPrice).Mul(openAmount).Mul(leverage) - orders.Orders[i].FuturesOrder.UnrealisedPNL = changeInPosition - orders.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = changeInPosition - orders.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ + snapshot.Orders[i].FuturesOrder.UnrealisedPNL = changeInPosition + snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = changeInPosition + snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ Time: e.GetTime(), UnrealisedPNL: changeInPosition, }) diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 845ddb5b622..4219fe10c2e 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -44,15 +44,15 @@ type Portfolio struct { // Handler contains all functions expected to operate a portfolio manager type Handler interface { - OnSignal(signal.Event, *exchange.Settings, funding.IPairReserver) (*order.Order, error) - OnFill(fill.Event, funding.IPairReader) (*fill.Fill, error) + OnSignal(signal.Event, *exchange.Settings, funding.IFundReserver) (*order.Order, error) + OnFill(fill.Event, funding.IFundReleaser) (*fill.Fill, error) GetLatestOrderSnapshotForEvent(common.EventHandler) (compliance.Snapshot, error) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) ViewHoldingAtTimePeriod(common.EventHandler) (*holdings.Holding, error) setHoldingsForOffset(*holdings.Holding, bool) error - UpdateHoldings(common.DataEventHandler, funding.IPairReader) error + UpdateHoldings(common.DataEventHandler, funding.IFundReleaser) error GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go index 746874beedf..3605d1e9028 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go @@ -67,7 +67,7 @@ func (s *Strategy) SupportsSimultaneousProcessing() bool { type mfiFundEvent struct { event signal.Event mfi decimal.Decimal - funds funding.IPairReader + funds funding.IFundReader } // ByPrice used for sorting orders by order date @@ -152,7 +152,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf mfiFundEvents = append(mfiFundEvents, mfiFundEvent{ event: &es, mfi: latestMFI, - funds: funds, + funds: funds.FundReader(), }) } diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go new file mode 100644 index 00000000000..650b4ea401d --- /dev/null +++ b/backtester/funding/collateral.go @@ -0,0 +1,50 @@ +package funding + +import ( + "errors" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +var ( + // ErrNotCollateral is returned when a user requests collateral from a non-collateral pair + ErrNotCollateral = errors.New("not a collateral pair") +) + +func (c *Collateral) CanPlaceOrder(_ order.Side) bool { + return c.Collateral.CanPlaceOrder() +} + +func (c *Collateral) TakeProfit(contracts, originalPositionSize, positionReturns decimal.Decimal) error { + err := c.Contract.Release(contracts, decimal.Zero) + if err != nil { + return err + } + return c.Collateral.Release(originalPositionSize, positionReturns) +} + +func (c *Collateral) ContractCurrency() currency.Code { + return c.Contract.currency +} + +func (c *Collateral) CollateralCurrency() currency.Code { + return c.Collateral.currency +} + +func (c *Collateral) InitialFunds() decimal.Decimal { + return c.Collateral.initialFunds +} + +func (c *Collateral) AvailableFunds() decimal.Decimal { + return c.Collateral.available +} + +func (c *Collateral) GetPairReader() (IPairReader, error) { + return nil, ErrNotPair +} + +func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { + return c, nil +} diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 2927fe38fa6..5b11a179529 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -14,7 +14,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) var ( @@ -24,8 +23,7 @@ var ( ErrAlreadyExists = errors.New("funding already exists") // ErrUSDTrackingDisabled used when attempting to track USD values when disabled ErrUSDTrackingDisabled = errors.New("USD tracking disabled") - // ErrNotCollateral is returned when a user requests collateral from a non-collateral pair - ErrNotCollateral = errors.New("not a collateral pair") + errCannotAllocate = errors.New("cannot allocate funds") errZeroAmountReceived = errors.New("amount received less than or equal to zero") errNegativeAmountReceived = errors.New("received negative decimal") @@ -44,6 +42,15 @@ func SetupFundingManager(usingExchangeLevelFunding, disableUSDTracking bool) *Fu } } +// CreateFuturesCurrencyCode converts a currency pair into a code +// The main reasoning is that as a contract, it exists as an item even if +// it is formatted as BTC-1231. To treat it as a pair in the funding system +// would cause an increase in funds for BTC, when it is an increase in contracts +// This function is basic, but is important be explicit in why this is occurring +func CreateFuturesCurrencyCode(b, q currency.Code) currency.Code { + return currency.NewCode(fmt.Sprintf("%s-%s", b, q)) +} + // CreateItem creates a new funding item func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, transferFee decimal.Decimal) (*Item, error) { if initialFunds.IsNegative() { @@ -331,22 +338,12 @@ func (f *FundManager) IsUsingExchangeLevelFunding() bool { } // GetFundingForEvent This will construct a funding based on a backtesting event -func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (*Pair, error) { +func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, error) { return f.GetFundingForEAP(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) } -// GetFundingForEAC This will construct a funding based on the exchange, asset, currency code -func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Code) (*Item, error) { - for i := range f.items { - if f.items[i].BasicEqual(exch, a, c, currency.Code{}) { - return f.items[i], nil - } - } - return nil, ErrFundsNotFound -} - // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair -func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (*Pair, error) { +func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { var resp Pair for i := range f.items { if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { @@ -366,207 +363,12 @@ func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pai return &resp, nil } -// BaseInitialFunds returns the initial funds -// from the base in a currency pair -func (p *Pair) BaseInitialFunds() decimal.Decimal { - return p.Base.initialFunds -} - -// QuoteInitialFunds returns the initial funds -// from the quote in a currency pair -func (p *Pair) QuoteInitialFunds() decimal.Decimal { - return p.Quote.initialFunds -} - -// BaseAvailable returns the available funds -// from the base in a currency pair -func (p *Pair) BaseAvailable() decimal.Decimal { - return p.Base.available -} - -// QuoteAvailable returns the available funds -// from the quote in a currency pair -func (p *Pair) QuoteAvailable() decimal.Decimal { - return p.Quote.available -} - -func (p *Pair) GetPairReader() (IPairReader, error) { - return p, nil -} - -func (p *Pair) GetCollateralReader() (ICollateralReader, error) { - return nil, ErrNotCollateral -} - -// Reserve allocates an amount of funds to be used at a later time -// it prevents multiple events from claiming the same resource -// changes which currency to affect based on the order side -func (p *Pair) Reserve(amount decimal.Decimal, side order.Side) error { - switch side { - case order.Buy: - return p.Quote.Reserve(amount) - case order.Sell: - return p.Base.Reserve(amount) - default: - return fmt.Errorf("%w for %v %v %v. Unknown side %v", - errCannotAllocate, - p.Base.exchange, - p.Base.asset, - p.Base.currency, - side) - } -} - -// Release reduces the amount of funding reserved and adds any difference -// back to the available amount -// changes which currency to affect based on the order side -func (p *Pair) Release(amount, diff decimal.Decimal, side order.Side) error { - switch side { - case order.Buy: - return p.Quote.Release(amount, diff) - case order.Sell: - return p.Base.Release(amount, diff) - default: - return fmt.Errorf("%w for %v %v %v. Unknown side %v", - errCannotAllocate, - p.Base.exchange, - p.Base.asset, - p.Base.currency, - side) - } -} - -// IncreaseAvailable adds funding to the available amount -// changes which currency to affect based on the order side -func (p *Pair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { - switch side { - case order.Buy: - p.Base.IncreaseAvailable(amount) - case order.Sell: - p.Quote.IncreaseAvailable(amount) - } -} - -// CanPlaceOrder does a > 0 check to see if there are any funds -// to place an order with -// changes which currency to affect based on the order side -func (p *Pair) CanPlaceOrder(side order.Side) bool { - switch side { - case order.Buy: - return p.Quote.CanPlaceOrder() - case order.Sell: - return p.Base.CanPlaceOrder() - } - return false -} - -func (c *CollateralPair) CanPlaceOrder(_ order.Side) bool { - return c.Collateral.CanPlaceOrder() -} - -// Reserve allocates an amount of funds to be used at a later time -// it prevents multiple events from claiming the same resource -func (i *Item) Reserve(amount decimal.Decimal) error { - if amount.LessThanOrEqual(decimal.Zero) { - return errZeroAmountReceived - } - if amount.GreaterThan(i.available) { - return fmt.Errorf("%w for %v %v %v. Requested %v Available: %v", - errCannotAllocate, - i.exchange, - i.asset, - i.currency, - amount, - i.available) - } - i.available = i.available.Sub(amount) - i.reserved = i.reserved.Add(amount) - return nil -} - -// Release reduces the amount of funding reserved and adds any difference -// back to the available amount -func (i *Item) Release(amount, diff decimal.Decimal) error { - if amount.LessThanOrEqual(decimal.Zero) { - return errZeroAmountReceived - } - if diff.IsNegative() { - return fmt.Errorf("%w diff", errNegativeAmountReceived) - } - if amount.GreaterThan(i.reserved) { - return fmt.Errorf("%w for %v %v %v. Requested %v Reserved: %v", - errCannotAllocate, - i.exchange, - i.asset, - i.currency, - amount, - i.reserved) - } - i.reserved = i.reserved.Sub(amount) - i.available = i.available.Add(diff) - return nil -} - -// IncreaseAvailable adds funding to the available amount -func (i *Item) IncreaseAvailable(amount decimal.Decimal) { - if amount.IsNegative() || amount.IsZero() { - return - } - i.available = i.available.Add(amount) -} - -// CanPlaceOrder checks if the item has any funds available -func (i *Item) CanPlaceOrder() bool { - return i.available.GreaterThan(decimal.Zero) -} - -// Equal checks for equality via an Item to compare to -func (i *Item) Equal(item *Item) bool { - if i == nil && item == nil { - return true - } - if item == nil || i == nil { - return false - } - if i.currency == item.currency && - i.asset == item.asset && - i.exchange == item.exchange { - if i.pairedWith == nil && item.pairedWith == nil { - return true - } - if i.pairedWith == nil || item.pairedWith == nil { - return false - } - if i.pairedWith.currency == item.pairedWith.currency && - i.pairedWith.asset == item.pairedWith.asset && - i.pairedWith.exchange == item.pairedWith.exchange { - return true +// GetFundingForEAC This will construct a funding based on the exchange, asset, currency code +func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Code) (*Item, error) { + for i := range f.items { + if f.items[i].BasicEqual(exch, a, c, currency.Code{}) { + return f.items[i], nil } } - return false -} - -// BasicEqual checks for equality via passed in values -func (i *Item) BasicEqual(exch string, a asset.Item, currency, pairedCurrency currency.Code) bool { - return i != nil && - i.exchange == exch && - i.asset == a && - i.currency == currency && - (i.pairedWith == nil || - (i.pairedWith != nil && i.pairedWith.currency == pairedCurrency)) -} - -// MatchesCurrency checks that an item's currency is equal -func (i *Item) MatchesCurrency(c currency.Code) bool { - return i != nil && i.currency == c -} - -// MatchesItemCurrency checks that an item's currency is equal -func (i *Item) MatchesItemCurrency(item *Item) bool { - return i != nil && item != nil && i.currency == item.currency -} - -// MatchesExchange checks that an item's exchange is equal -func (i *Item) MatchesExchange(item *Item) bool { - return i != nil && item != nil && i.exchange == item.exchange + return nil, ErrFundsNotFound } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index ca023119ffa..d336154efa5 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -24,8 +24,8 @@ type IFundingManager interface { Reset() IsUsingExchangeLevelFunding() bool GetFundingForEAC(string, asset.Item, currency.Code) (*Item, error) - GetFundingForEvent(common.EventHandler) (*Pair, error) - GetFundingForEAP(string, asset.Item, currency.Pair) (*Pair, error) + GetFundingForEvent(common.EventHandler) (IFundingPair, error) + GetFundingForEAP(string, asset.Item, currency.Pair) (IFundingPair, error) Transfer(decimal.Decimal, *Item, *Item, bool) error GenerateReport() *Report AddUSDTrackingData(*kline.DataFromKline) error @@ -33,14 +33,44 @@ type IFundingManager interface { USDTrackingDisabled() bool } +// IFundingPair allows conversion into various +// funding interfaces +type IFundingPair interface { + FundReader() IFundReader + FundReserver() IFundReserver + FundReleaser() IFundReleaser +} + +// IFundReader allows a connoisseur to read +// either collateral or pair details +type IFundReader interface { + GetPairReader() (IPairReader, error) + GetCollateralReader() (ICollateralReader, error) +} + // IFundTransferer allows for funding amounts to be transferred // implementation can be swapped for live transferring type IFundTransferer interface { IsUsingExchangeLevelFunding() bool Transfer(decimal.Decimal, *Item, *Item, bool) error GetFundingForEAC(string, asset.Item, currency.Code) (*Item, error) - GetFundingForEvent(common.EventHandler) (*Pair, error) - GetFundingForEAP(string, asset.Item, currency.Pair) (*Pair, error) + GetFundingForEvent(common.EventHandler) (IFundingPair, error) + GetFundingForEAP(string, asset.Item, currency.Pair) (IFundingPair, error) +} + +// IFundReserver limits funding usage for portfolio event handling +type IFundReserver interface { + IFundReader + CanPlaceOrder(order.Side) bool + Reserve(decimal.Decimal, order.Side) error +} + +// IFundReleaser allows a connoisseur to read +// or release pair or collateral funds +type IFundReleaser interface { + IFundReader + GetPairReleaser() (IPairReleaser, error) + GetCollateralReleaser() (ICollateralReleaser, error) } // IPairReader is used to limit pair funding functions @@ -52,27 +82,26 @@ type IPairReader interface { QuoteAvailable() decimal.Decimal } +// ICollateralReader is used to read data from +// collateral pairs type ICollateralReader interface { - Currency() currency.Code + ContractCurrency() currency.Code + CollateralCurrency() currency.Code InitialFunds() decimal.Decimal AvailableFunds() decimal.Decimal } -// IPairReserver limits funding usage for portfolio event handling -type IPairReserver interface { - CanPlaceOrder(order.Side) bool - Reserve(decimal.Decimal, order.Side) error - GetPairReader() (IPairReader, error) - GetCollateralReader() (ICollateralReader, error) -} - // IPairReleaser limits funding usage for exchange event handling type IPairReleaser interface { + IPairReader IncreaseAvailable(decimal.Decimal, order.Side) Release(decimal.Decimal, decimal.Decimal, order.Side) error } type ICollateralReleaser interface { + ICollateralReader + TakeProfit(decimal.Decimal, decimal.Decimal) error + ReleaseContracts(decimal.Decimal) error } // Item holds funding data per currency item @@ -97,10 +126,10 @@ type Pair struct { Quote *Item } -// CollateralPair consists of a currency pair for a futures contract +// Collateral consists of a currency pair for a futures contract // and associates it with an addition collateral pair to take funding from -type CollateralPair struct { - Contract *Pair +type Collateral struct { + Contract *Item Collateral *Item } diff --git a/backtester/funding/item.go b/backtester/funding/item.go new file mode 100644 index 00000000000..9dad3509c2d --- /dev/null +++ b/backtester/funding/item.go @@ -0,0 +1,116 @@ +package funding + +import ( + "fmt" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +// Reserve allocates an amount of funds to be used at a later time +// it prevents multiple events from claiming the same resource +func (i *Item) Reserve(amount decimal.Decimal) error { + if amount.LessThanOrEqual(decimal.Zero) { + return errZeroAmountReceived + } + if amount.GreaterThan(i.available) { + return fmt.Errorf("%w for %v %v %v. Requested %v Available: %v", + errCannotAllocate, + i.exchange, + i.asset, + i.currency, + amount, + i.available) + } + i.available = i.available.Sub(amount) + i.reserved = i.reserved.Add(amount) + return nil +} + +// Release reduces the amount of funding reserved and adds any difference +// back to the available amount +func (i *Item) Release(amount, diff decimal.Decimal) error { + if amount.LessThanOrEqual(decimal.Zero) { + return errZeroAmountReceived + } + if diff.IsNegative() && i.asset == asset.Spot { + return fmt.Errorf("%w diff", errNegativeAmountReceived) + } + if amount.GreaterThan(i.reserved) { + return fmt.Errorf("%w for %v %v %v. Requested %v Reserved: %v", + errCannotAllocate, + i.exchange, + i.asset, + i.currency, + amount, + i.reserved) + } + i.reserved = i.reserved.Sub(amount) + i.available = i.available.Add(diff) + return nil +} + +// IncreaseAvailable adds funding to the available amount +func (i *Item) IncreaseAvailable(amount decimal.Decimal) { + if amount.IsNegative() || amount.IsZero() { + return + } + i.available = i.available.Add(amount) +} + +// CanPlaceOrder checks if the item has any funds available +func (i *Item) CanPlaceOrder() bool { + return i.available.GreaterThan(decimal.Zero) +} + +// Equal checks for equality via an Item to compare to +func (i *Item) Equal(item *Item) bool { + if i == nil && item == nil { + return true + } + if item == nil || i == nil { + return false + } + if i.currency == item.currency && + i.asset == item.asset && + i.exchange == item.exchange { + if i.pairedWith == nil && item.pairedWith == nil { + return true + } + if i.pairedWith == nil || item.pairedWith == nil { + return false + } + if i.pairedWith.currency == item.pairedWith.currency && + i.pairedWith.asset == item.pairedWith.asset && + i.pairedWith.exchange == item.pairedWith.exchange { + return true + } + } + return false +} + +// BasicEqual checks for equality via passed in values +func (i *Item) BasicEqual(exch string, a asset.Item, currency, pairedCurrency currency.Code) bool { + return i != nil && + i.exchange == exch && + i.asset == a && + i.currency == currency && + (i.pairedWith == nil || + (i.pairedWith != nil && i.pairedWith.currency == pairedCurrency)) +} + +// MatchesCurrency checks that an item's currency is equal +func (i *Item) MatchesCurrency(c currency.Code) bool { + return i != nil && i.currency == c +} + +// MatchesItemCurrency checks that an item's currency is equal +func (i *Item) MatchesItemCurrency(item *Item) bool { + return i != nil && item != nil && i.currency == item.currency +} + +// MatchesExchange checks that an item's exchange is equal +func (i *Item) MatchesExchange(item *Item) bool { + return i != nil && item != nil && i.exchange == item.exchange +} diff --git a/backtester/funding/pair.go b/backtester/funding/pair.go new file mode 100644 index 00000000000..caa55e0c02f --- /dev/null +++ b/backtester/funding/pair.go @@ -0,0 +1,133 @@ +package funding + +import ( + "errors" + "fmt" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +var ( + // ErrNotPair is returned when a user requests funding pair details when it is a collateral pair + ErrNotPair = errors.New("not a funding pair") +) + +// BaseInitialFunds returns the initial funds +// from the base in a currency pair +func (p *Pair) BaseInitialFunds() decimal.Decimal { + return p.Base.initialFunds +} + +// QuoteInitialFunds returns the initial funds +// from the quote in a currency pair +func (p *Pair) QuoteInitialFunds() decimal.Decimal { + return p.Quote.initialFunds +} + +// BaseAvailable returns the available funds +// from the base in a currency pair +func (p *Pair) BaseAvailable() decimal.Decimal { + return p.Base.available +} + +// QuoteAvailable returns the available funds +// from the quote in a currency pair +func (p *Pair) QuoteAvailable() decimal.Decimal { + return p.Quote.available +} + +func (p *Pair) GetPairReader() (IPairReader, error) { + return p, nil +} + +func (p *Pair) GetCollateralReader() (ICollateralReader, error) { + return nil, ErrNotCollateral +} + +// Reserve allocates an amount of funds to be used at a later time +// it prevents multiple events from claiming the same resource +// changes which currency to affect based on the order side +func (p *Pair) Reserve(amount decimal.Decimal, side order.Side) error { + switch side { + case order.Buy: + return p.Quote.Reserve(amount) + case order.Sell: + return p.Base.Reserve(amount) + default: + return fmt.Errorf("%w for %v %v %v. Unknown side %v", + errCannotAllocate, + p.Base.exchange, + p.Base.asset, + p.Base.currency, + side) + } +} + +// Release reduces the amount of funding reserved and adds any difference +// back to the available amount +// changes which currency to affect based on the order side +func (p *Pair) Release(amount, diff decimal.Decimal, side order.Side) error { + switch side { + case order.Buy: + return p.Quote.Release(amount, diff) + case order.Sell: + return p.Base.Release(amount, diff) + default: + return fmt.Errorf("%w for %v %v %v. Unknown side %v", + errCannotAllocate, + p.Base.exchange, + p.Base.asset, + p.Base.currency, + side) + } +} + +// IncreaseAvailable adds funding to the available amount +// changes which currency to affect based on the order side +func (p *Pair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { + switch side { + case order.Buy: + p.Base.IncreaseAvailable(amount) + case order.Sell: + p.Quote.IncreaseAvailable(amount) + } +} + +// CanPlaceOrder does a > 0 check to see if there are any funds +// to place an order with +// changes which currency to affect based on the order side +func (p *Pair) CanPlaceOrder(side order.Side) bool { + switch side { + case order.Buy: + return p.Quote.CanPlaceOrder() + case order.Sell: + return p.Base.CanPlaceOrder() + } + return false +} + +// FundReader +func (p *Pair) FundReader() IFundReader { + return p +} + +// FundReserver +func (p *Pair) FundReserver() IFundReserver { + return p +} + +// GetPairReleaser +func (p *Pair) GetPairReleaser() (IPairReleaser, error) { + return p, nil +} + +// GetCollateralReleaser +func (p *Pair) GetCollateralReleaser() (ICollateralReleaser, error) { + return nil, ErrNotCollateral +} + +// FundReleaser +func (p *Pair) FundReleaser() IFundReleaser { + return p +} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index dc5e30e34a0..e19f6599e2d 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -118,13 +118,14 @@ type ModifyResponse struct { // Futures order is a concept which holds both the opening and closing orders // for a futures contract. This allows for PNL calculations type Futures struct { - Side Side - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - UnderlyingAsset currency.Code - OpeningPosition *Detail - ClosingPosition *Detail - PNLHistory []PNLHistory + Side Side + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal + UnderlyingAsset currency.Code + CollateralCurrency currency.Code + OpeningPosition *Detail + ClosingPosition *Detail + PNLHistory []PNLHistory } // UpsertPNLEntry upserts an entry to PNLHistory field From 4621183d889dfe6d4ae5d0b57dc1d4309f7e8527 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 9 Nov 2021 17:17:36 +1100 Subject: [PATCH 022/171] Implements more collateral interfaces --- backtester/funding/collateral.go | 33 +++++++++++++++++++++++++++++ backtester/funding/funding_types.go | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 650b4ea401d..7e67e7f2d74 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -48,3 +48,36 @@ func (c *Collateral) GetPairReader() (IPairReader, error) { func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { return c, nil } + +func (c *Collateral) Reserve(amount decimal.Decimal, _ order.Side) error { + return c.Collateral.Reserve(amount) +} + +func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { + return c.Contract.Release(amount, decimal.Zero) +} + +// FundReader +func (c *Collateral) FundReader() IFundReader { + return c +} + +// FundReserver +func (c *Collateral) FundReserver() IFundReserver { + return c +} + +// GetPairReleaser +func (c *Collateral) GetPairReleaser() (IPairReleaser, error) { + return nil, ErrNotPair +} + +// GetCollateralReleaser +func (c *Collateral) GetCollateralReleaser() (ICollateralReleaser, error) { + return c, nil +} + +// FundReleaser +func (c *Collateral) FundReleaser() IFundReleaser { + return c +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index d336154efa5..17a0d8eb702 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -100,7 +100,7 @@ type IPairReleaser interface { type ICollateralReleaser interface { ICollateralReader - TakeProfit(decimal.Decimal, decimal.Decimal) error + TakeProfit(decimal.Decimal, decimal.Decimal, decimal.Decimal) error ReleaseContracts(decimal.Decimal) error } From 273e82a876bbd65a531efcd6e2d018921c34c12d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 10 Nov 2021 16:21:58 +1100 Subject: [PATCH 023/171] Adds liquidation, adds strategy, struggles with Binance --- backtester/backtest/backtest.go | 16 +++- backtester/common/common_types.go | 1 + backtester/config/config.go | 11 ++- backtester/config/config_test.go | 77 ++++++++++++++++- backtester/config/config_types.go | 3 +- ...a-api-candles-exchange-level-funding.strat | 21 ++--- .../dca-api-candles-multiple-currencies.strat | 22 ++--- ...-api-candles-simultaneous-processing.strat | 22 ++--- .../config/examples/dca-api-candles.strat | 11 +-- .../config/examples/dca-api-trades.strat | 11 +-- .../config/examples/dca-candles-live.strat | 11 +-- .../config/examples/dca-csv-candles.strat | 11 +-- .../config/examples/dca-csv-trades.strat | 11 +-- .../examples/dca-database-candles.strat | 11 +-- .../config/examples/futures-api-candles.strat | 82 +++++++++++++++++++ .../config/examples/rsi-api-candles.strat | 24 ++---- .../t2b2-api-candles-exchange-funding.strat | 60 ++++---------- .../eventhandlers/exchange/exchange_types.go | 2 +- .../eventhandlers/portfolio/portfolio.go | 14 +++- .../portfolio/portfolio_types.go | 2 +- .../eventhandlers/portfolio/risk/risk.go | 3 +- .../portfolio/risk/risk_types.go | 2 +- backtester/funding/collateral.go | 5 ++ backtester/funding/funding.go | 25 ++++++ backtester/funding/funding_types.go | 2 + backtester/main.go | 2 +- 26 files changed, 304 insertions(+), 158 deletions(-) create mode 100644 backtester/config/examples/futures-api-candles.strat diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index d1cf4dbaae7..5d666aa705f 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -268,7 +268,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { return nil, err } - case asset.Futures: + case asset.Futures, asset.USDTMarginedFutures, asset.CoinMarginedFutures: // setup contract items c := funding.CreateFuturesCurrencyCode(b, q) futureItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, @@ -1022,10 +1022,18 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu log.Error(log.BackTester, err) } - err = bt.Portfolio.CalculatePNL(ev) - if err != nil { - log.Error(log.BackTester, err) + if ev.GetAssetType() == asset.Futures { + var cr funding.ICollateralReleaser + cr, err = funds.GetCollateralReleaser() + if err != nil { + return err + } + err = bt.Portfolio.CalculatePNL(ev, cr) + if err != nil { + log.Error(log.BackTester, err) + } } + return nil } diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 97766cab0ef..75071005c13 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -28,6 +28,7 @@ const ( CouldNotCloseShort order.Side = "COULD NOT CLOSE SHORT" CouldNotCloseLong order.Side = "COULD NOT CLOSE LONG" + Liquidated order.Side = "LIQUIDATED" // MissingData is signalled during the strategy/signal phase when data has been identified as missing // No buy or sell events can occur MissingData order.Side = "MISSING DATA" diff --git a/backtester/config/config.go b/backtester/config/config.go index 57c9d2434ed..8686f8d095c 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -273,8 +273,17 @@ func (c *Config) validateCurrencySettings() error { return errNoCurrencySettings } for i := range c.CurrencySettings { + if c.CurrencySettings[i].Asset == asset.PerpetualSwap.String() || + c.CurrencySettings[i].Asset == asset.PerpetualContract.String() { + return errPerpetualsUnsupported + } if c.CurrencySettings[i].SpotDetails == nil && c.CurrencySettings[i].FuturesDetails == nil { - return fmt.Errorf("%w please add spot or future currency details", errNoCurrencySettings) + return fmt.Errorf("%w please add spot or future currency details or create a new config via the config builder", errNoCurrencySettings) + } + if c.CurrencySettings[i].FuturesDetails != nil { + if c.CurrencySettings[i].Quote == "PERP" || c.CurrencySettings[i].Base == "PI" { + return errPerpetualsUnsupported + } } if c.CurrencySettings[i].SpotDetails != nil { // if c.CurrencySettings[i].SpotDetails.InitialLegacyFunds > 0 { diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index a72c05e1320..44cdac83901 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -24,7 +24,7 @@ const ( testExchange = "binance" dca = "dollarcostaverage" // change this if you modify a config and want it to save to the example folder - saveConfig = false + saveConfig = !false ) var ( @@ -1197,3 +1197,78 @@ func TestValidate(t *testing.T) { t.Errorf("received %v expected %v", err, nil) } } + +func TestGenerateConfigForFuturesAPICandles(t *testing.T) { + cfg := Config{ + Nickname: "ExampleStrategyFuturesAPICandles", + Goal: "To demonstrate DCA strategy using API candles", + StrategySettings: StrategySettings{ + Name: dca, + UseExchangeLevelFunding: true, + SimultaneousSignalProcessing: true, + DisableUSDTracking: true, + ExchangeLevelFunding: []ExchangeLevelFunding{ + { + ExchangeName: testExchange, + Asset: asset.USDTMarginedFutures.String(), + Currency: "usdt", + InitialFunds: *initialQuoteFunds2, + TransferFee: decimal.Zero, + Collateral: true, + }, + }, + }, + CurrencySettings: []CurrencySettings{ + { + ExchangeName: testExchange, + Asset: asset.USDTMarginedFutures.String(), + Base: "BTC", + Quote: "USDT211231", + FuturesDetails: &FuturesDetails{ + CollateralCurrency: "USDT", + Leverage: Leverage{ + CanUseLeverage: true, + MaximumLeverageRate: 1, + }, + }, + BuySide: minMax, + SellSide: minMax, + MakerFee: makerFee, + TakerFee: takerFee, + }, + }, + DataSettings: DataSettings{ + Interval: kline.OneDay.Duration(), + DataType: common.CandleStr, + APIData: &APIData{ + StartDate: startDate, + EndDate: endDate, + InclusiveEndDate: false, + }, + }, + PortfolioSettings: PortfolioSettings{ + BuySide: minMax, + SellSide: minMax, + Leverage: Leverage{ + CanUseLeverage: true, + }, + }, + StatisticSettings: StatisticSettings{ + RiskFreeRate: decimal.NewFromFloat(0.03), + }, + } + if saveConfig { + result, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + t.Fatal(err) + } + p, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + err = ioutil.WriteFile(filepath.Join(p, "examples", "futures-api-candles.strat"), result, 0770) + if err != nil { + t.Error(err) + } + } +} diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 763bf1c1228..4ec14ceb6de 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -24,6 +24,7 @@ var ( errSizeLessThanZero = errors.New("size less than zero") errMaxSizeMinSizeMismatch = errors.New("maximum size must be greater to minimum size") errMinMaxEqual = errors.New("minimum and maximum limits cannot be equal") + errPerpetualsUnsupported = errors.New("perpetual futures not yet supported") ) // Config defines what is in an individual strategy config @@ -98,7 +99,7 @@ type PortfolioSettings struct { type Leverage struct { CanUseLeverage bool `json:"can-use-leverage"` MaximumOrdersWithLeverageRatio decimal.Decimal `json:"maximum-orders-with-leverage-ratio"` - MaximumLeverageRate decimal.Decimal `json:"maximum-leverage-rate"` + MaximumLeverageRate float64 `json:"maximum-leverage-rate"` } // MinMax are the rules which limit the placement of orders. diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index 7cb4692293d..fad3137e532 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -11,7 +11,8 @@ "asset": "spot", "currency": "USDT", "initial-funds": "100000", - "transfer-fee": "0" + "transfer-fee": "0", + "collateral": false } ], "disable-usd-tracking": true @@ -22,11 +23,6 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -42,19 +38,14 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "ETH", "quote": "USDT", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -70,8 +61,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-api-candles-multiple-currencies.strat b/backtester/config/examples/dca-api-candles-multiple-currencies.strat index 26a52309d28..976c4685fb9 100644 --- a/backtester/config/examples/dca-api-candles-multiple-currencies.strat +++ b/backtester/config/examples/dca-api-candles-multiple-currencies.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -34,19 +31,16 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "ETH", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -63,8 +57,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat index 8e92da7bf1f..f826bb1ad76 100644 --- a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat +++ b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "1000000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "1000000" }, "buy-side": { "minimum-size": "0.005", @@ -34,19 +31,16 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "ETH", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -63,8 +57,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-api-candles.strat b/backtester/config/examples/dca-api-candles.strat index bd55e24ee2e..304eb3b7b40 100644 --- a/backtester/config/examples/dca-api-candles.strat +++ b/backtester/config/examples/dca-api-candles.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -34,8 +31,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-api-trades.strat b/backtester/config/examples/dca-api-trades.strat index 285595ba7ea..fd148ecdb48 100644 --- a/backtester/config/examples/dca-api-trades.strat +++ b/backtester/config/examples/dca-api-trades.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -34,8 +31,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": true + "skip-candle-volume-fitting": true, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-candles-live.strat b/backtester/config/examples/dca-candles-live.strat index 5ebbd139b55..edbf88d1b02 100644 --- a/backtester/config/examples/dca-candles-live.strat +++ b/backtester/config/examples/dca-candles-live.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -34,8 +31,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-csv-candles.strat b/backtester/config/examples/dca-csv-candles.strat index a5556b0c5ba..88cd4225e08 100644 --- a/backtester/config/examples/dca-csv-candles.strat +++ b/backtester/config/examples/dca-csv-candles.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -34,8 +31,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-csv-trades.strat b/backtester/config/examples/dca-csv-trades.strat index c8b16f661fd..e421e740aa9 100644 --- a/backtester/config/examples/dca-csv-trades.strat +++ b/backtester/config/examples/dca-csv-trades.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0", @@ -34,8 +31,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/dca-database-candles.strat b/backtester/config/examples/dca-database-candles.strat index 2b5182c6447..49a4ead8cf1 100644 --- a/backtester/config/examples/dca-database-candles.strat +++ b/backtester/config/examples/dca-database-candles.strat @@ -13,11 +13,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -34,8 +31,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/futures-api-candles.strat new file mode 100644 index 00000000000..cadc63467e8 --- /dev/null +++ b/backtester/config/examples/futures-api-candles.strat @@ -0,0 +1,82 @@ +{ + "nickname": "ExampleStrategyFuturesAPICandles", + "goal": "To demonstrate DCA strategy using API candles", + "strategy-settings": { + "name": "dollarcostaverage", + "use-simultaneous-signal-processing": true, + "use-exchange-level-funding": true, + "exchange-level-funding": [ + { + "exchange-name": "binance", + "asset": "usdtmarginedfutures", + "currency": "usdt", + "initial-funds": "100000", + "transfer-fee": "0", + "collateral": true + } + ], + "disable-usd-tracking": true + }, + "currency-settings": [ + { + "exchange-name": "binance", + "asset": "usdtmarginedfutures", + "base": "BTC", + "quote": "USDT211231", + "futures-details": { + "leverage": { + "can-use-leverage": true, + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": 1 + }, + "collateral-currency": "USDT" + }, + "buy-side": { + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" + }, + "sell-side": { + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" + }, + "min-slippage-percent": "0", + "max-slippage-percent": "0", + "maker-fee-override": "0.001", + "taker-fee-override": "0.002", + "maximum-holdings-ratio": "0", + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false + } + ], + "data-settings": { + "interval": 86400000000000, + "data-type": "candle", + "api-data": { + "start-date": "2020-08-01T00:00:00+10:00", + "end-date": "2020-12-01T00:00:00+11:00", + "inclusive-end-date": false + } + }, + "portfolio-settings": { + "leverage": { + "can-use-leverage": true, + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": 0 + }, + "buy-side": { + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" + }, + "sell-side": { + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" + } + }, + "statistic-settings": { + "risk-free-rate": "0.03" + } +} \ No newline at end of file diff --git a/backtester/config/examples/rsi-api-candles.strat b/backtester/config/examples/rsi-api-candles.strat index d8ff88b1453..b01e41f9b98 100644 --- a/backtester/config/examples/rsi-api-candles.strat +++ b/backtester/config/examples/rsi-api-candles.strat @@ -18,11 +18,8 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "initial-quote-funds": "100000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-quote-funds": "100000" }, "buy-side": { "minimum-size": "0.005", @@ -39,20 +36,17 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "ETH", "quote": "USDT", - "initial-base-funds": "10", - "initial-quote-funds": "1000000", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "spot-details": { + "initial-base-funds": "10", + "initial-quote-funds": "1000000" }, "buy-side": { "minimum-size": "0.005", @@ -69,8 +63,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 1f4b3b9256c..a275822ad08 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -11,14 +11,16 @@ "asset": "spot", "currency": "BTC", "initial-funds": "3", - "transfer-fee": "0" + "transfer-fee": "0", + "collateral": false }, { "exchange-name": "binance", "asset": "spot", "currency": "USDT", "initial-funds": "10000", - "transfer-fee": "0" + "transfer-fee": "0", + "collateral": false } ], "disable-usd-tracking": false, @@ -34,11 +36,6 @@ "asset": "spot", "base": "BTC", "quote": "USDT", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -54,19 +51,14 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "DOGE", "quote": "USDT", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -82,19 +74,14 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "ETH", "quote": "BTC", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -110,19 +97,14 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "LTC", "quote": "BTC", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -138,19 +120,14 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "XRP", "quote": "USDT", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -166,19 +143,14 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false }, { "exchange-name": "binance", "asset": "spot", "base": "BNB", "quote": "BTC", - "leverage": { - "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" - }, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", @@ -194,8 +166,8 @@ "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "use-exchange-order-limits": false, - "skip-candle-volume-fitting": false + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false } ], "data-settings": { diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index 8b12cca7c51..af582cdeacb 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -71,5 +71,5 @@ type MinMax struct { type Leverage struct { CanUseLeverage bool MaximumOrdersWithLeverageRatio decimal.Decimal - MaximumLeverageRate decimal.Decimal + MaximumLeverageRate float64 } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 4b9a2a0d320..debb3f9a51f 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -537,7 +537,7 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // CalculatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL -func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { +func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.ICollateralReleaser) error { snapshot, err := p.GetLatestOrderSnapshotForEvent(e) if err != nil { return err @@ -549,15 +549,25 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { if snapshot.Orders[i].FuturesOrder.ClosingPosition != nil { continue } - if snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage == 0 { snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 } + leverage := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage) openPrice := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Price) openAmount := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount) changeInPosition := e.ClosePrice().Sub(openPrice).Mul(openAmount).Mul(leverage) + // determine if we've been liquidated + if changeInPosition.IsNegative() && changeInPosition.Abs().GreaterThanOrEqual(funds.AvailableFunds()) { + funds.Liquidate() + snapshot.Orders[i].FuturesOrder.UnrealisedPNL = decimal.Zero + snapshot.Orders[i].FuturesOrder.RealisedPNL = decimal.Zero + or := snapshot.Orders[i].FuturesOrder.OpeningPosition.Copy() + or.Side = common.Liquidated + snapshot.Orders[i].FuturesOrder.ClosingPosition = &or + return nil + } snapshot.Orders[i].FuturesOrder.UnrealisedPNL = changeInPosition snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = changeInPosition snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 4219fe10c2e..660620f6dd9 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -59,7 +59,7 @@ type Handler interface { SetFee(string, asset.Item, currency.Pair, decimal.Decimal) GetFee(string, asset.Item, currency.Pair) decimal.Decimal - CalculatePNL(common.DataEventHandler) error + CalculatePNL(common.DataEventHandler, funding.ICollateralReleaser) error Reset() } diff --git a/backtester/eventhandlers/portfolio/risk/risk.go b/backtester/eventhandlers/portfolio/risk/risk.go index 6dc4a63560b..c141e328558 100644 --- a/backtester/eventhandlers/portfolio/risk/risk.go +++ b/backtester/eventhandlers/portfolio/risk/risk.go @@ -37,7 +37,8 @@ func (r *Risk) EvaluateOrder(o order.Event, latestHoldings []holdings.Holding, s if ratio.GreaterThan(lookup.MaximumOrdersWithLeverageRatio) && lookup.MaximumOrdersWithLeverageRatio.GreaterThan(decimal.Zero) { return nil, fmt.Errorf("proceeding with the order would put maximum orders using leverage ratio beyond its limit of %v to %v and %w", lookup.MaximumOrdersWithLeverageRatio, ratio, errCannotPlaceLeverageOrder) } - if retOrder.GetLeverage().GreaterThan(lookup.MaxLeverageRate) && lookup.MaxLeverageRate.GreaterThan(decimal.Zero) { + lr := decimal.NewFromFloat(lookup.MaxLeverageRate) + if retOrder.GetLeverage().GreaterThan(lr) && lr.GreaterThan(decimal.Zero) { return nil, fmt.Errorf("proceeding with the order would put leverage rate beyond its limit of %v to %v and %w", lookup.MaxLeverageRate, retOrder.GetLeverage(), errCannotPlaceLeverageOrder) } } diff --git a/backtester/eventhandlers/portfolio/risk/risk_types.go b/backtester/eventhandlers/portfolio/risk/risk_types.go index d3c1d5b92a9..4f673325b47 100644 --- a/backtester/eventhandlers/portfolio/risk/risk_types.go +++ b/backtester/eventhandlers/portfolio/risk/risk_types.go @@ -32,6 +32,6 @@ type Risk struct { // CurrencySettings contains relevant limits to assess risk type CurrencySettings struct { MaximumOrdersWithLeverageRatio decimal.Decimal - MaxLeverageRate decimal.Decimal + MaxLeverageRate float64 MaximumHoldingRatio decimal.Decimal } diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 7e67e7f2d74..f438e8fe2cf 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -81,3 +81,8 @@ func (c *Collateral) GetCollateralReleaser() (ICollateralReleaser, error) { func (c *Collateral) FundReleaser() IFundReleaser { return c } + +func (c *Collateral) Liquidate() { + c.Collateral.available = decimal.Zero + c.Contract.available = decimal.Zero +} diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 5b11a179529..b58fba81259 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -372,3 +372,28 @@ func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Cod } return nil, ErrFundsNotFound } + +// LiquidateByCollateral will remove all collateral value +// and all contracts +func (f *FundManager) LiquidateByCollateral(c currency.Code) error { + found := false + for i := range f.items { + if f.items[i].currency == c && f.items[i].collateral && f.items[i].asset == asset.Futures { + f.items[i].available = decimal.Zero + f.items[i].reserved = decimal.Zero + found = true + } + } + if !found { + return ErrNotCollateral + } + for i := range f.items { + if f.items[i].pairedWith != nil && + f.items[i].pairedWith.currency == c && + f.items[i].asset == asset.Futures { + f.items[i].available = decimal.Zero + f.items[i].reserved = decimal.Zero + } + } + return nil +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 17a0d8eb702..440daa14908 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -31,6 +31,7 @@ type IFundingManager interface { AddUSDTrackingData(*kline.DataFromKline) error CreateSnapshot(time.Time) USDTrackingDisabled() bool + LiquidateByCollateral(currency.Code) error } // IFundingPair allows conversion into various @@ -102,6 +103,7 @@ type ICollateralReleaser interface { ICollateralReader TakeProfit(decimal.Decimal, decimal.Decimal, decimal.Decimal) error ReleaseContracts(decimal.Decimal) error + Liquidate() } // Item holds funding data per currency item diff --git a/backtester/main.go b/backtester/main.go index f19cf14c7c7..14929db30b8 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -28,7 +28,7 @@ func main() { wd, "config", "examples", - "dca-api-candles.strat"), + "futures-api-candles.strat"), "the config containing strategy params") flag.StringVar( &templatePath, From 93df00e13670d0bc3712b6bfa8b024d98d0fe070 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 12 Nov 2021 15:58:43 +1100 Subject: [PATCH 024/171] Attempts at getting FTX to work --- backtester/backtest/backtest.go | 7 ++++++- backtester/config/config_test.go | 14 +++++++------- .../dca-api-candles-exchange-level-funding.strat | 2 +- .../dca-api-candles-multiple-currencies.strat | 2 +- .../dca-api-candles-simultaneous-processing.strat | 2 +- backtester/config/examples/dca-api-candles.strat | 2 +- backtester/config/examples/dca-api-trades.strat | 2 +- backtester/config/examples/dca-candles-live.strat | 2 +- backtester/config/examples/dca-csv-candles.strat | 2 +- backtester/config/examples/dca-csv-trades.strat | 2 +- .../config/examples/dca-database-candles.strat | 2 +- .../config/examples/futures-api-candles.strat | 14 +++++++------- backtester/config/examples/rsi-api-candles.strat | 2 +- .../t2b2-api-candles-exchange-funding.strat | 2 +- 14 files changed, 31 insertions(+), 26 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 5d666aa705f..77f3f20bbac 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -217,7 +217,12 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, } curr = curr.Format(requestFormat.Delimiter, requestFormat.Uppercase) err = exchBase.CurrencyPairs.EnablePair(a, curr) - if err != nil && !errors.Is(err, currency.ErrPairAlreadyEnabled) { + if errors.Is(err, currency.ErrPairNotFound) { + exchBase.CurrencyPairs.Pairs[a].Available.Add(curr) + exchBase.CurrencyPairs.Pairs[a].Enabled.Add(curr) + exch.SetPairs(exchBase.CurrencyPairs.Pairs[a].Enabled, a, true) + exch.SetPairs(exchBase.CurrencyPairs.Pairs[a].Available, a, false) + return nil, fmt.Errorf( "could not enable currency %v %v %v. Err %w", cfg.CurrencySettings[i].ExchangeName, diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 44cdac83901..0d8ccb54416 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1209,8 +1209,8 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { DisableUSDTracking: true, ExchangeLevelFunding: []ExchangeLevelFunding{ { - ExchangeName: testExchange, - Asset: asset.USDTMarginedFutures.String(), + ExchangeName: "ftx", + Asset: asset.Futures.String(), Currency: "usdt", InitialFunds: *initialQuoteFunds2, TransferFee: decimal.Zero, @@ -1220,10 +1220,10 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { }, CurrencySettings: []CurrencySettings{ { - ExchangeName: testExchange, - Asset: asset.USDTMarginedFutures.String(), + ExchangeName: "ftx", + Asset: asset.Futures.String(), Base: "BTC", - Quote: "USDT211231", + Quote: "0924", FuturesDetails: &FuturesDetails{ CollateralCurrency: "USDT", Leverage: Leverage{ @@ -1241,8 +1241,8 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { Interval: kline.OneDay.Duration(), DataType: common.CandleStr, APIData: &APIData{ - StartDate: startDate, - EndDate: endDate, + StartDate: time.Date(2021, 1, 14, 0, 0, 0, 0, time.UTC), + EndDate: time.Date(2021, 9, 24, 0, 0, 0, 0, time.UTC), InclusiveEndDate: false, }, }, diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index fad3137e532..b6874f002f4 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -78,7 +78,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles-multiple-currencies.strat b/backtester/config/examples/dca-api-candles-multiple-currencies.strat index 976c4685fb9..b8a7ab5b60c 100644 --- a/backtester/config/examples/dca-api-candles-multiple-currencies.strat +++ b/backtester/config/examples/dca-api-candles-multiple-currencies.strat @@ -74,7 +74,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat index f826bb1ad76..9af74be31f6 100644 --- a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat +++ b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat @@ -74,7 +74,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles.strat b/backtester/config/examples/dca-api-candles.strat index 304eb3b7b40..510550a3b0e 100644 --- a/backtester/config/examples/dca-api-candles.strat +++ b/backtester/config/examples/dca-api-candles.strat @@ -48,7 +48,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-trades.strat b/backtester/config/examples/dca-api-trades.strat index fd148ecdb48..6610b791736 100644 --- a/backtester/config/examples/dca-api-trades.strat +++ b/backtester/config/examples/dca-api-trades.strat @@ -48,7 +48,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.1", diff --git a/backtester/config/examples/dca-candles-live.strat b/backtester/config/examples/dca-candles-live.strat index edbf88d1b02..ef809d5befc 100644 --- a/backtester/config/examples/dca-candles-live.strat +++ b/backtester/config/examples/dca-candles-live.strat @@ -51,7 +51,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-csv-candles.strat b/backtester/config/examples/dca-csv-candles.strat index 88cd4225e08..c5e1d1a345d 100644 --- a/backtester/config/examples/dca-csv-candles.strat +++ b/backtester/config/examples/dca-csv-candles.strat @@ -46,7 +46,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-csv-trades.strat b/backtester/config/examples/dca-csv-trades.strat index e421e740aa9..5ef096fcc3f 100644 --- a/backtester/config/examples/dca-csv-trades.strat +++ b/backtester/config/examples/dca-csv-trades.strat @@ -46,7 +46,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0", diff --git a/backtester/config/examples/dca-database-candles.strat b/backtester/config/examples/dca-database-candles.strat index 49a4ead8cf1..37548ce3f52 100644 --- a/backtester/config/examples/dca-database-candles.strat +++ b/backtester/config/examples/dca-database-candles.strat @@ -62,7 +62,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/futures-api-candles.strat index cadc63467e8..8917307b707 100644 --- a/backtester/config/examples/futures-api-candles.strat +++ b/backtester/config/examples/futures-api-candles.strat @@ -7,8 +7,8 @@ "use-exchange-level-funding": true, "exchange-level-funding": [ { - "exchange-name": "binance", - "asset": "usdtmarginedfutures", + "exchange-name": "ftx", + "asset": "futures", "currency": "usdt", "initial-funds": "100000", "transfer-fee": "0", @@ -19,10 +19,10 @@ }, "currency-settings": [ { - "exchange-name": "binance", - "asset": "usdtmarginedfutures", + "exchange-name": "ftx", + "asset": "futures", "base": "BTC", - "quote": "USDT211231", + "quote": "0924", "futures-details": { "leverage": { "can-use-leverage": true, @@ -54,8 +54,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-01-14T00:00:00Z", + "end-date": "2021-09-24T00:00:00Z", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/rsi-api-candles.strat b/backtester/config/examples/rsi-api-candles.strat index b01e41f9b98..80c9693ed55 100644 --- a/backtester/config/examples/rsi-api-candles.strat +++ b/backtester/config/examples/rsi-api-candles.strat @@ -80,7 +80,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index a275822ad08..92a5e4385dc 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -183,7 +183,7 @@ "leverage": { "can-use-leverage": false, "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": "0" + "maximum-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", From 6eafb6afffe08507dae11c476b334136dff5b76b Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 15 Nov 2021 13:24:48 +1100 Subject: [PATCH 025/171] Adds calculatePNL as a wrapper function and adds an `IsFutures` asset check --- backtester/backtest/backtest.go | 11 +--- .../eventhandlers/portfolio/portfolio.go | 59 ++++++++++++------ .../eventhandlers/portfolio/portfolio_test.go | 62 +++++++++++-------- .../portfolio/portfolio_types.go | 2 + exchanges/asset/asset.go | 10 +++ exchanges/exchange.go | 46 ++++++++++++++ exchanges/interfaces.go | 4 +- exchanges/order/order_types.go | 1 + 8 files changed, 138 insertions(+), 57 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 77f3f20bbac..74615bfbe2a 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -403,19 +403,10 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, bt.Exchange = &e for i := range e.CurrencySettings { - var lookup *portfolio.Settings - - lookup, err = p.SetupCurrencySettingsMap(&e.CurrencySettings[i]) + err = p.SetupCurrencySettingsMap(&e.CurrencySettings[i], emm[e.CurrencySettings[i].Exchange]) if err != nil { return nil, err } - lookup.Fee = e.CurrencySettings[i].TakerFee - lookup.Leverage = e.CurrencySettings[i].Leverage - lookup.BuySideSizing = e.CurrencySettings[i].BuySide - lookup.SellSideSizing = e.CurrencySettings[i].SellSide - lookup.ComplianceManager = compliance.Manager{ - Snapshots: []compliance.Snapshot{}, - } } bt.Portfolio = p diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index debb3f9a51f..646b684f38a 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -17,6 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" + gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" @@ -483,18 +484,18 @@ func (p *Portfolio) ViewHoldingAtTimePeriod(ev common.EventHandler) (*holdings.H } // SetupCurrencySettingsMap ensures a map is created and no panics happen -func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings) (*Settings, error) { +func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch gctexchange.IBotExchange) error { if settings == nil { - return nil, errNoPortfolioSettings + return errNoPortfolioSettings } if settings.Exchange == "" { - return nil, errExchangeUnset + return errExchangeUnset } if settings.Asset == "" { - return nil, errAssetUnset + return errAssetUnset } if settings.Pair.IsEmpty() { - return nil, errCurrencyPairUnset + return errCurrencyPairUnset } if p.exchangeAssetPairSettings == nil { p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) @@ -505,11 +506,20 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings) (*Sett if p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] == nil { p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] = make(map[currency.Pair]*Settings) } - if _, ok := p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair]; !ok { - p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair] = &Settings{} + if _, ok := p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair]; ok { + return nil } - - return p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair], nil + p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair] = &Settings{ + Fee: settings.ExchangeFee, + BuySideSizing: settings.BuySide, + SellSideSizing: settings.SellSide, + Leverage: settings.Leverage, + ComplianceManager: compliance.Manager{ + Snapshots: []compliance.Snapshot{}, + }, + Exchange: exch, + } + return nil } // GetLatestHoldings returns the latest holdings after being sorted by time @@ -538,6 +548,10 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // CalculatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.ICollateralReleaser) error { + settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] + if !ok { + return errNoPortfolioSettings + } snapshot, err := p.GetLatestOrderSnapshotForEvent(e) if err != nil { return err @@ -553,13 +567,18 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 } - leverage := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage) - openPrice := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Price) - openAmount := decimal.NewFromFloat(snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount) - - changeInPosition := e.ClosePrice().Sub(openPrice).Mul(openAmount).Mul(leverage) - // determine if we've been liquidated - if changeInPosition.IsNegative() && changeInPosition.Abs().GreaterThanOrEqual(funds.AvailableFunds()) { + result, err := settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ + Asset: e.GetAssetType(), + Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, + OpeningPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, + OpeningAmount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, + CurrentPrice: e.ClosePrice().InexactFloat64(), + Collateral: funds.AvailableFunds(), + }) + if err != nil { + return err + } + if result.IsLiquidated { funds.Liquidate() snapshot.Orders[i].FuturesOrder.UnrealisedPNL = decimal.Zero snapshot.Orders[i].FuturesOrder.RealisedPNL = decimal.Zero @@ -568,13 +587,13 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla snapshot.Orders[i].FuturesOrder.ClosingPosition = &or return nil } - snapshot.Orders[i].FuturesOrder.UnrealisedPNL = changeInPosition - snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = changeInPosition + + snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.PNL + snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = result.PNL snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ Time: e.GetTime(), - UnrealisedPNL: changeInPosition, + UnrealisedPNL: result.PNL, }) - } return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index ba410c51946..c78c38cfd65 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -66,27 +66,27 @@ func TestSetup(t *testing.T) { func TestSetupCurrencySettingsMap(t *testing.T) { t.Parallel() p := &Portfolio{} - _, err := p.SetupCurrencySettingsMap(nil) + err := p.SetupCurrencySettingsMap(nil, nil) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{}, nil) if !errors.Is(err, errExchangeUnset) { t.Errorf("received: %v, expected: %v", err, errExchangeUnset) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi"}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi"}, nil) if !errors.Is(err, errAssetUnset) { t.Errorf("received: %v, expected: %v", err, errAssetUnset) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot}, nil) if !errors.Is(err, errCurrencyPairUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyPairUnset) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -107,7 +107,7 @@ func TestSetHoldings(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -147,7 +147,7 @@ func TestGetLatestHoldingsForAllCurrencies(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -209,7 +209,7 @@ func TestViewHoldingAtTimePeriod(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoHoldings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -282,7 +282,7 @@ func TestUpdate(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -308,7 +308,7 @@ func TestGetFee(t *testing.T) { t.Error("expected 0") } - _, err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -328,7 +328,7 @@ func TestGetComplianceManager(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -355,7 +355,7 @@ func TestAddComplianceSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -401,7 +401,7 @@ func TestOnFill(t *testing.T) { if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -478,7 +478,7 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -525,7 +525,7 @@ func TestOnSignal(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - _, err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) if err != nil { t.Error(err) } @@ -572,11 +572,15 @@ func TestGetSnapshotAtTime(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } cp := currency.NewPair(currency.XRP, currency.DOGE) - s, err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "exch", Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "exch", Asset: asset.Spot, Pair: cp}, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } tt := time.Now() + s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][cp] + if !ok { + t.Fatal("couldn't get settings") + } err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { SpotOrder: &gctorder.Detail{ @@ -620,11 +624,15 @@ func TestGetLatestSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } cp := currency.NewPair(currency.XRP, currency.DOGE) - s, err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "exch", Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "exch", Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } tt := time.Now() + s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][cp] + if !ok { + t.Fatal("couldn't get settings") + } err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { SpotOrder: &gctorder.Detail{ @@ -679,7 +687,7 @@ func TestCalculatePNL(t *testing.T) { } ev := &kline.Kline{} - err := p.CalculatePNL(ev) + err := p.CalculatePNL(ev, nil) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } @@ -687,12 +695,12 @@ func TestCalculatePNL(t *testing.T) { exch := "binance" a := asset.Futures pair, _ := currency.NewPairFromStrings("BTC", "1231") - s, err := p.SetupCurrencySettingsMap(&exchange.Settings{ + err = p.SetupCurrencySettingsMap(&exchange.Settings{ Exchange: exch, UseRealOrders: false, Pair: pair, Asset: a, - }) + }, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -703,7 +711,7 @@ func TestCalculatePNL(t *testing.T) { ev.CurrencyPair = pair ev.Time = tt0 - err = p.CalculatePNL(ev) + err = p.CalculatePNL(ev, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -720,7 +728,10 @@ func TestCalculatePNL(t *testing.T) { Pair: pair, }, } - + s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][pair] + if !ok { + t.Fatal("couldn't get settings") + } ev.Close = decimal.NewFromInt(1337) err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { @@ -728,7 +739,7 @@ func TestCalculatePNL(t *testing.T) { FuturesOrder: futuresOrder, }, }, tt0, 0, false) - err = p.CalculatePNL(ev) + err = p.CalculatePNL(ev, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -748,20 +759,21 @@ func TestCalculatePNL(t *testing.T) { SpotOrder: futuresOrder.OpeningPosition, }, }, tt, 1, false) - err = p.CalculatePNL(ev) + err = p.CalculatePNL(ev, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } // coverage of logic futuresOrder.ClosingPosition = futuresOrder.OpeningPosition + err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { ClosePrice: decimal.NewFromInt(1336), FuturesOrder: futuresOrder, }, }, tt.Add(time.Hour), 2, false) - err = p.CalculatePNL(ev) + err = p.CalculatePNL(ev, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 660620f6dd9..6884829527d 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -14,6 +14,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" + gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -78,4 +79,5 @@ type Settings struct { Leverage exchange.Leverage HoldingsSnapshots []holdings.Holding ComplianceManager compliance.Manager + Exchange gctexchange.IBotExchange } diff --git a/exchanges/asset/asset.go b/exchanges/asset/asset.go index a12e7b000cc..772cd991753 100644 --- a/exchanges/asset/asset.go +++ b/exchanges/asset/asset.go @@ -117,3 +117,13 @@ func New(input string) (Item, error) { func UseDefault() Item { return Spot } + +// IsFutures checks if the asset type is a futures contract based asset +func (a Item) IsFutures() bool { + switch a { + case PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, + DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures: + return true + } + return false +} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index c2bae861b3a..84923611bcf 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" @@ -1459,3 +1460,48 @@ func (b *Base) UpdateCurrencyStates(ctx context.Context, a asset.Item) error { func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([]string, error) { return nil, common.ErrFunctionNotSupported } + +type PNLCalculator struct { + OrderID string + Asset asset.Item + Leverage float64 + OpeningPrice float64 + OpeningAmount float64 + CurrentPrice float64 + Collateral decimal.Decimal +} + +type PNLResult struct { + PNL decimal.Decimal + IsLiquidated bool +} + +var ErrUnsetLeverage error + +// CalculatePNL is an overridable function to allow PNL to be calculated on an +// open position +// It will also determine whether the position is considered to be liquidated +// for live trading, an overrided function may wish to confirm the liquidation by +// requesting the status of the asset +func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { + if !calc.Asset.IsFutures() { + return nil, common.ErrFunctionNotSupported + } + if calc.Leverage == 0 { + // you really should have set this earlier + return nil, ErrUnsetLeverage + } + var result *PNLResult + cp := decimal.NewFromFloat(calc.CurrentPrice) + op := decimal.NewFromFloat(calc.OpeningPrice) + lv := decimal.NewFromFloat(calc.Leverage) + result.PNL = cp.Sub(op).Mul(op).Mul(lv) + if result.PNL.IsNegative() && calc.Collateral.LessThanOrEqual(result.PNL.Abs()) { + // calculating whether something is liquidated changes per exchange + // If your chosen exchange has its own liquidation formula, please ensure + // it is implemented there rather than rely on this base function + result.IsLiquidated = true + } + + return result, nil +} diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index e97baecddb7..a5ea3ceed68 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -80,6 +80,8 @@ type IBotExchange interface { GetHistoricCandlesExtended(ctx context.Context, p currency.Pair, a asset.Item, timeStart, timeEnd time.Time, interval kline.Interval) (kline.Item, error) DisableRateLimiter() error EnableRateLimiter() error + CalculatePNL(*PNLCalculator) (*PNLResult, error) + CurrencyStateManagement GetWebsocket() (*stream.Websocket, error) IsWebsocketEnabled() bool @@ -93,8 +95,6 @@ type IBotExchange interface { GetOrderExecutionLimits(a asset.Item, cp currency.Pair) (*order.Limits, error) CheckOrderExecutionLimits(a asset.Item, cp currency.Pair, price, amount float64, orderType order.Type) error UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error - - CurrencyStateManagement } // CurrencyStateManagement defines functionality for currency state management diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index e19f6599e2d..25938ae94af 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -125,6 +125,7 @@ type Futures struct { CollateralCurrency currency.Code OpeningPosition *Detail ClosingPosition *Detail + ClosingPositions []Detail PNLHistory []PNLHistory } From 0d0ef7f395aeec58db944b097bd1cc0fc2983a4e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 16 Nov 2021 15:38:12 +1100 Subject: [PATCH 026/171] Successfully loads backtester with collateral currency --- backtester/backtest/backtest.go | 57 ++++++++++++++++++++++----------- backtester/funding/funding.go | 39 ++++++++++++++++++---- backtester/funding/item.go | 2 +- 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 74615bfbe2a..b7c671a65be 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -133,7 +133,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, cq, cfg.StrategySettings.ExchangeLevelFunding[i].InitialFunds, - cfg.StrategySettings.ExchangeLevelFunding[i].TransferFee) + cfg.StrategySettings.ExchangeLevelFunding[i].TransferFee, + cfg.StrategySettings.ExchangeLevelFunding[i].Collateral) if err != nil { return nil, err } @@ -216,20 +217,27 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, return nil, fmt.Errorf("could not format currency %v, %w", curr, err) } curr = curr.Format(requestFormat.Delimiter, requestFormat.Uppercase) - err = exchBase.CurrencyPairs.EnablePair(a, curr) - if errors.Is(err, currency.ErrPairNotFound) { - exchBase.CurrencyPairs.Pairs[a].Available.Add(curr) - exchBase.CurrencyPairs.Pairs[a].Enabled.Add(curr) - exch.SetPairs(exchBase.CurrencyPairs.Pairs[a].Enabled, a, true) - exch.SetPairs(exchBase.CurrencyPairs.Pairs[a].Available, a, false) + var avail, enab currency.Pairs + avail, err = exch.GetAvailablePairs(a) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + enab, err = exch.GetEnabledPairs(a) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } - return nil, fmt.Errorf( - "could not enable currency %v %v %v. Err %w", - cfg.CurrencySettings[i].ExchangeName, - cfg.CurrencySettings[i].Asset, - cfg.CurrencySettings[i].Base+cfg.CurrencySettings[i].Quote, - err) + avail = avail.Add(curr) + enab = enab.Add(curr) + err = exch.SetPairs(enab, a, true) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) } + err = exch.SetPairs(avail, a, false) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + portSet := &risk.CurrencySettings{ MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, } @@ -253,7 +261,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, b, decimal.Zero, - decimal.Zero) + decimal.Zero, + false) if err != nil { return nil, err } @@ -261,7 +270,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, q, decimal.Zero, - decimal.Zero) + decimal.Zero, + false) if err != nil { return nil, err } @@ -280,7 +290,15 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, c, decimal.Zero, - decimal.Zero) + decimal.Zero, + false) + if err != nil { + return nil, err + } + if cfg.CurrencySettings[i].FuturesDetails == nil { + return nil, errors.New("what the fuck") + } + err = funds.LinkCollateralCurrency(futureItem, currency.NewCode(cfg.CurrencySettings[i].FuturesDetails.CollateralCurrency)) if err != nil { return nil, err } @@ -288,6 +306,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, if err != nil { return nil, err } + default: return nil, fmt.Errorf("%w %v unsupported", errInvalidConfigAsset, a) } @@ -306,7 +325,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, curr.Base, bFunds, - decimal.Zero) + decimal.Zero, + false) if err != nil { return nil, err } @@ -315,7 +335,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, curr.Quote, qFunds, - decimal.Zero) + decimal.Zero, + false) if err != nil { return nil, err } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index b58fba81259..c897685876a 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -31,6 +31,7 @@ var ( errCannotTransferToSameFunds = errors.New("cannot send funds to self") errTransferMustBeSameCurrency = errors.New("cannot transfer to different currency") errCannotMatchTrackingToItem = errors.New("cannot match tracking data to funding items") + errNotFutures = errors.New("item linking collateral currencies must be a futures asset") ) // SetupFundingManager creates the funding holder. It carries knowledge about levels of funding @@ -52,7 +53,7 @@ func CreateFuturesCurrencyCode(b, q currency.Code) currency.Code { } // CreateItem creates a new funding item -func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, transferFee decimal.Decimal) (*Item, error) { +func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, transferFee decimal.Decimal, isCollateral bool) (*Item, error) { if initialFunds.IsNegative() { return nil, fmt.Errorf("%v %v %v %w initial funds: %v", exch, a, ci, errNegativeAmountReceived, initialFunds) } @@ -68,9 +69,25 @@ func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, trans available: initialFunds, transferFee: transferFee, snapshot: make(map[time.Time]ItemSnapshot), + collateral: isCollateral, }, nil } +// LinkCollateralCurrency links an item to an existing currency code +// for collateral purposes +func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) error { + if !item.asset.IsFutures() { + return errNotFutures + } + for i := range f.items { + if f.items[i].currency.Match(code) && f.items[i].asset == item.asset { + item.pairedWith = f.items[i] + return nil + } + } + return ErrFundsNotFound +} + // CreateSnapshot creates a Snapshot for an event's point in time // as funding.snapshots is a map, it allows for the last event // in the chronological list to establish the canon at X time @@ -345,13 +362,21 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { var resp Pair + for i := range f.items { - if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { - resp.Base = f.items[i] - continue - } - if f.items[i].BasicEqual(exch, a, p.Quote, p.Base) { - resp.Quote = f.items[i] + if a.IsFutures() { + if f.items[i].BasicEqual(exch, a, currency.NewCode(p.String()), currency.USDT) { + resp.Base = f.items[i] + resp.Quote = f.items[i].pairedWith + } + } else { + if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { + resp.Base = f.items[i] + continue + } + if f.items[i].BasicEqual(exch, a, p.Quote, p.Base) { + resp.Quote = f.items[i] + } } } if resp.Base == nil { diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 9dad3509c2d..7714d47659e 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -97,7 +97,7 @@ func (i *Item) BasicEqual(exch string, a asset.Item, currency, pairedCurrency cu i.asset == a && i.currency == currency && (i.pairedWith == nil || - (i.pairedWith != nil && i.pairedWith.currency == pairedCurrency)) + (i.pairedWith != nil && i.pairedWith.currency.Match(pairedCurrency))) } // MatchesCurrency checks that an item's currency is equal From 00131fbe1c15a35bfd1d883f4c6709045977e8e6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 17 Nov 2021 17:18:50 +1100 Subject: [PATCH 027/171] Fails to really get much going for supporting futures --- .../portfolio/holdings/holdings.go | 2 +- .../eventhandlers/portfolio/portfolio.go | 6 +---- backtester/funding/collateral.go | 5 ++-- backtester/funding/funding.go | 24 ++++++++++--------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index af44b095370..1db3d00998d 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -9,7 +9,7 @@ import ( ) // Create makes a Holding struct to track total values of strategy holdings over the course of a backtesting run -func Create(ev ClosePriceReader, funding funding.IPairReader) (Holding, error) { +func Create(ev ClosePriceReader, funding funding.IFundReader) (Holding, error) { if ev == nil { return Holding{}, common.ErrNilEvent } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 646b684f38a..7da9fcd7492 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -402,11 +402,7 @@ func (p *Portfolio) UpdateHoldings(ev common.DataEventHandler, funds funding.IFu ev.Pair()) } h := lookup.GetLatestHoldings() - if h.Timestamp.IsZero() && ev.GetAssetType() == asset.Spot { - f, err := funds.GetPairReader() - if err != nil { - return err - } + if h.Timestamp.IsZero() { h, err = holdings.Create(ev, f) if err != nil { return err diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index f438e8fe2cf..642de4090f7 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -2,6 +2,7 @@ package funding import ( "errors" + "fmt" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" @@ -42,7 +43,7 @@ func (c *Collateral) AvailableFunds() decimal.Decimal { } func (c *Collateral) GetPairReader() (IPairReader, error) { - return nil, ErrNotPair + return nil, fmt.Errorf("could not return pair reader for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { @@ -69,7 +70,7 @@ func (c *Collateral) FundReserver() IFundReserver { // GetPairReleaser func (c *Collateral) GetPairReleaser() (IPairReleaser, error) { - return nil, ErrNotPair + return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } // GetCollateralReleaser diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index c897685876a..f77ac0eef82 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -361,15 +361,16 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { - var resp Pair - for i := range f.items { if a.IsFutures() { + var resp Collateral if f.items[i].BasicEqual(exch, a, currency.NewCode(p.String()), currency.USDT) { - resp.Base = f.items[i] - resp.Quote = f.items[i].pairedWith + resp.Contract = f.items[i] + resp.Collateral = f.items[i].pairedWith + return &resp, nil } } else { + var resp Pair if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { resp.Base = f.items[i] continue @@ -377,15 +378,16 @@ func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pai if f.items[i].BasicEqual(exch, a, p.Quote, p.Base) { resp.Quote = f.items[i] } + if resp.Base == nil { + return nil, fmt.Errorf("base %w", ErrFundsNotFound) + } + if resp.Quote == nil { + return nil, fmt.Errorf("quote %w", ErrFundsNotFound) + } + return &resp, nil } } - if resp.Base == nil { - return nil, fmt.Errorf("base %w", ErrFundsNotFound) - } - if resp.Quote == nil { - return nil, fmt.Errorf("quote %w", ErrFundsNotFound) - } - return &resp, nil + return nil, fmt.Errorf("%v %v %v %w", exch, a, p, ErrFundsNotFound) } // GetFundingForEAC This will construct a funding based on the exchange, asset, currency code From 148cfaf94fa156ad32249a969859c832f54de76c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 19 Nov 2021 14:42:34 +1100 Subject: [PATCH 028/171] Merges master changes --- .../portfolio/holdings/holdings.go | 42 ++++++++++++------- .../eventhandlers/portfolio/portfolio.go | 9 ++-- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index 1db3d00998d..c5d3d676095 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -5,30 +5,40 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) // Create makes a Holding struct to track total values of strategy holdings over the course of a backtesting run -func Create(ev ClosePriceReader, funding funding.IFundReader) (Holding, error) { +func Create(ev ClosePriceReader, fundReader funding.IFundReader) (Holding, error) { if ev == nil { return Holding{}, common.ErrNilEvent } - if funding.QuoteInitialFunds().LessThan(decimal.Zero) { - return Holding{}, ErrInitialFundsZero - } + if ev.GetAssetType().IsFutures() { - return Holding{ - Offset: ev.GetOffset(), - Pair: ev.Pair(), - Asset: ev.GetAssetType(), - Exchange: ev.GetExchange(), - Timestamp: ev.GetTime(), - QuoteInitialFunds: funding.QuoteInitialFunds(), - QuoteSize: funding.QuoteInitialFunds(), - BaseInitialFunds: funding.BaseInitialFunds(), - BaseSize: funding.BaseInitialFunds(), - TotalInitialValue: funding.QuoteInitialFunds().Add(funding.BaseInitialFunds().Mul(ev.GetClosePrice())), - }, nil + } else { + funds, err := fundReader.GetPairReader() + if err != nil { + return Holding{}, err + } + if funds.QuoteInitialFunds().LessThan(decimal.Zero) { + return Holding{}, ErrInitialFundsZero + } + + return Holding{ + Offset: ev.GetOffset(), + Pair: ev.Pair(), + Asset: ev.GetAssetType(), + Exchange: ev.GetExchange(), + Timestamp: ev.GetTime(), + QuoteInitialFunds: funds.QuoteInitialFunds(), + QuoteSize: funds.QuoteInitialFunds(), + BaseInitialFunds: funds.BaseInitialFunds(), + BaseSize: funds.BaseInitialFunds(), + TotalInitialValue: funds.QuoteInitialFunds().Add(funds.BaseInitialFunds().Mul(ev.GetClosePrice())), + }, nil + } + return Holding{}, asset.ErrNotSupported } // Update calculates holding statistics for the events time diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 7da9fcd7492..5caac169e9c 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -245,7 +245,7 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. } else { h = lookup.GetLatestHoldings() if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, fp) + h, err = holdings.Create(ev, funding) if err != nil { return nil, err } @@ -402,14 +402,15 @@ func (p *Portfolio) UpdateHoldings(ev common.DataEventHandler, funds funding.IFu ev.Pair()) } h := lookup.GetLatestHoldings() + var err error if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, f) + h, err = holdings.Create(ev, funds) if err != nil { return err } } h.UpdateValue(ev) - err := p.setHoldingsForOffset(&h, true) + err = p.setHoldingsForOffset(&h, true) if errors.Is(err, errNoHoldings) { err = p.setHoldingsForOffset(&h, false) } @@ -568,7 +569,7 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, OpeningPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, OpeningAmount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, - CurrentPrice: e.ClosePrice().InexactFloat64(), + CurrentPrice: e.GetClosePrice().InexactFloat64(), Collateral: funds.AvailableFunds(), }) if err != nil { From 35161d627a78d711773f732a1dcb8ee2c4929bad Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 19 Nov 2021 17:05:42 +1100 Subject: [PATCH 029/171] Fleshes out how FTX processes collateral --- .../eventhandlers/portfolio/portfolio.go | 2 +- exchanges/exchange.go | 35 +-- exchanges/ftx/ftx.go | 215 ++++++++++++++++++ exchanges/ftx/ftx_types.go | 10 + exchanges/ftx/ftx_wrapper.go | 58 +++++ exchanges/order/orders.go | 4 + 6 files changed, 310 insertions(+), 14 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 5caac169e9c..b22ce4bbd5d 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -567,7 +567,7 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla result, err := settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ Asset: e.GetAssetType(), Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, - OpeningPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, + EntryPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, OpeningAmount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, CurrentPrice: e.GetClosePrice().InexactFloat64(), Collateral: funds.AvailableFunds(), diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 84923611bcf..bf67c0c9843 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -20,6 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/currencystate" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/stream" @@ -1462,18 +1463,26 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ } type PNLCalculator struct { - OrderID string - Asset asset.Item - Leverage float64 - OpeningPrice float64 - OpeningAmount float64 - CurrentPrice float64 - Collateral decimal.Decimal + CalculateOffline bool + Underlying currency.Code + OrderID string + Asset asset.Item + Side order.Side + Leverage float64 + EntryPrice float64 + OpeningAmount float64 + Amount float64 + CurrentPrice float64 + Collateral decimal.Decimal } type PNLResult struct { - PNL decimal.Decimal - IsLiquidated bool + MarginFraction decimal.Decimal + EstimatedLiquidationPrice decimal.Decimal + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal + Collateral decimal.Decimal + IsLiquidated bool } var ErrUnsetLeverage error @@ -1488,15 +1497,15 @@ func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { return nil, common.ErrFunctionNotSupported } if calc.Leverage == 0 { - // you really should have set this earlier + // you really should have set this earlier, idiot return nil, ErrUnsetLeverage } var result *PNLResult cp := decimal.NewFromFloat(calc.CurrentPrice) - op := decimal.NewFromFloat(calc.OpeningPrice) + op := decimal.NewFromFloat(calc.EntryPrice) lv := decimal.NewFromFloat(calc.Leverage) - result.PNL = cp.Sub(op).Mul(op).Mul(lv) - if result.PNL.IsNegative() && calc.Collateral.LessThanOrEqual(result.PNL.Abs()) { + result.UnrealisedPNL = cp.Sub(op).Mul(op).Mul(lv) + if result.UnrealisedPNL.IsNegative() && calc.Collateral.LessThanOrEqual(result.UnrealisedPNL.Abs()) { // calculating whether something is liquidated changes per exchange // If your chosen exchange has its own liquidation formula, please ensure // it is implemented there rather than rely on this base function diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index aee9cf84aeb..d5d2344ab12 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -26,6 +26,7 @@ import ( // FTX is the overarching type across this package type FTX struct { exchange.Base + collateralWeight CollateralWeightHolder } const ( @@ -1468,3 +1469,217 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err } return limits, nil } + +func (f *FTX) CalculateCollateral(code currency.Code, amount, price float64, isPositiveBalance bool) (float64, error) { + collateralWeight, ok := f.collateralWeight[code] + if !ok { + return 0, errCoinMustBeSpecified + } + if isPositiveBalance { + if collateralWeight.IMFFactor == 0 { + return 0, errCoinMustBeSpecified + } + return amount * price * math.Min(collateralWeight.Total, 1.1/collateralWeight.IMFFactor*math.Sqrt(amount)), nil + } + // im not sure this is what FTX means + return amount * price, nil +} + +func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64, side order.Side) (float64, error) { + collateralWeight, ok := f.collateralWeight[code] + if !ok { + return 0, errCoinMustBeSpecified + } + return collateralWeight.Total * positionSize, nil +} + +// LoadCollateralWeightings sets the collateral weights for +// currencies supported by FTX +func (f *FTX) LoadCollateralWeightings(useOfflineWeightings bool) error { + f.collateralWeight = make(map[currency.Code]CollateralWeight) + ctx := context.Background() + if useOfflineWeightings { + // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral + // not sure if ill keep this + f.collateralWeight.load(currency.NewCode("1INCH"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("AAPL"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("AAVE"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("ABNB"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("ACB"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("ALPHA"), 0.9, 0.85, 0.00025) + f.collateralWeight.load(currency.NewCode("AMC"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("AMD"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("AMZN"), 0.9, 0.85, 0.03) + f.collateralWeight.load(currency.NewCode("APHA"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("ARKK"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("AUD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("BABA"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("BADGER"), 0.85, 0.8, 0.0025) + f.collateralWeight.load(currency.NewCode("BAND"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("BAO"), 0.85, 0.8, 0.000025) + f.collateralWeight.load(currency.NewCode("BB"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("BCH"), 0.95, 0.9, 0.0008) + f.collateralWeight.load(currency.NewCode("BILI"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("BITW"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("BNB"), 0.95, 0.9, 0.0005) + f.collateralWeight.load(currency.NewCode("BNT"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("BNTX"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("BRL"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("BRZ"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("BTC"), 0.975, 0.95, 0.002) + f.collateralWeight.load(currency.NewCode("BTMX"), 0.7, 0.65, 0.0008) + f.collateralWeight.load(currency.NewCode("BUSD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("BVOL"), 0.85, 0.8, 0.005) + f.collateralWeight.load(currency.NewCode("BYND"), 0.9, 0.85, 0.0075) + f.collateralWeight.load(currency.NewCode("CAD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("CEL"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("CGC"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("CHF"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("COIN"), 0.85, 0.8, 0.01) + f.collateralWeight.load(currency.NewCode("COMP"), 0.9, 0.85, 0.002) + f.collateralWeight.load(currency.NewCode("COPE"), 0.6, 0.55, 0.02) + f.collateralWeight.load(currency.NewCode("CRON"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("CUSDT"), 0.9, 0.85, 0.00001) + f.collateralWeight.load(currency.NewCode("DAI"), 0.9, 0.85, 0.00005) + f.collateralWeight.load(currency.NewCode("DOGE"), 0.95, 0.9, 0.00002) + f.collateralWeight.load(currency.NewCode("ETH"), 0.95, 0.9, 0.0004) + f.collateralWeight.load(currency.NewCode("STETH"), 0.9, 0.85, 0.0012) + f.collateralWeight.load(currency.NewCode("ETHE"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("EUR"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("FB"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("FIDA"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("FTM"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("FTT"), 0.95, 0.95, 0.0005) + f.collateralWeight.load(currency.NewCode("GBP"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("GBTC"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("GDX"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("GDXJ"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GLD"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GLXY"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GME"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GOOGL"), 0.9, 0.85, 0.025) + f.collateralWeight.load(currency.NewCode("GRT"), 0.9, 0.85, 0.00025) + f.collateralWeight.load(currency.NewCode("HKD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("HOLY"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("HOOD"), 0.85, 0.8, 0.005) + f.collateralWeight.load(currency.NewCode("HT"), 0.9, 0.85, 0.0003) + f.collateralWeight.load(currency.NewCode("HUSD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("HXRO"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("IBVOL"), 0.85, 0.8, 0.015) + f.collateralWeight.load(currency.NewCode("KIN"), 0.85, 0.8, 0.000008) + f.collateralWeight.load(currency.NewCode("KNC"), 0.95, 0.9, 0.001) + f.collateralWeight.load(currency.NewCode("LEO"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("LINK"), 0.95, 0.9, 0.0004) + f.collateralWeight.load(currency.NewCode("LRC"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("LTC"), 0.95, 0.9, 0.0004) + f.collateralWeight.load(currency.NewCode("MATIC"), 0.85, 0.8, 0.00004) + f.collateralWeight.load(currency.NewCode("MKR"), 0.9, 0.85, 0.007) + f.collateralWeight.load(currency.NewCode("MOB"), 0.6, 0.55, 0.005) + f.collateralWeight.load(currency.NewCode("MRNA"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("MSTR"), 0.9, 0.85, 0.008) + f.collateralWeight.load(currency.NewCode("NFLX"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("NIO"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("NOK"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("NVDA"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("OKB"), 0.9, 0.85, 0.0003) + f.collateralWeight.load(currency.NewCode("OMG"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("USDP"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("PAXG"), 0.95, 0.9, 0.002) + f.collateralWeight.load(currency.NewCode("PENN"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("PFE"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("PYPL"), 0.9, 0.85, 0.008) + f.collateralWeight.load(currency.NewCode("RAY"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("REN"), 0.9, 0.85, 0.00025) + f.collateralWeight.load(currency.NewCode("RSR"), 0.85, 0.8, 0.0001) + f.collateralWeight.load(currency.NewCode("RUNE"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("SECO"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("SGD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("SLV"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("SNX"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("SOL"), 0.9, 0.85, 0.0004) + f.collateralWeight.load(currency.NewCode("STSOL"), 0.9, 0.85, 0.0004) + f.collateralWeight.load(currency.NewCode("MSOL"), 0.9, 0.85, 0.0004) + f.collateralWeight.load(currency.NewCode("SPY"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("SQ"), 0.9, 0.85, 0.008) + f.collateralWeight.load(currency.NewCode("SRM"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("SUSHI"), 0.95, 0.9, 0.001) + f.collateralWeight.load(currency.NewCode("SXP"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("TLRY"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("TOMO"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("TRX"), 0.9, 0.85, 0.00001) + f.collateralWeight.load(currency.NewCode("TRY"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("TRYB"), 0.9, 0.85, 0.00001) + f.collateralWeight.load(currency.NewCode("TSLA"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("TSM"), 0.9, 0.85, 0.015) + f.collateralWeight.load(currency.NewCode("TUSD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("TWTR"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("UBER"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("UNI"), 0.95, 0.9, 0.001) + f.collateralWeight.load(currency.NewCode("USD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("USDC"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("USDT"), 0.975, 0.95, 0.00001) + f.collateralWeight.load(currency.NewCode("USO"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("WBTC"), 0.975, 0.95, 0.005) + f.collateralWeight.load(currency.NewCode("WUSDC"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("WUSDT"), 0.975, 0.95, 0.00001) + f.collateralWeight.load(currency.NewCode("XAUT"), 0.95, 0.9, 0.002) + f.collateralWeight.load(currency.NewCode("XRP"), 0.95, 0.9, 0.00002) + f.collateralWeight.load(currency.NewCode("YFI"), 0.9, 0.85, 0.015) + f.collateralWeight.load(currency.NewCode("ZAR"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("ZM"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("ZRX"), 0.85, 0.8, 0.001) + } else { + coins, err := f.GetCoins(ctx) + if err != nil { + return err + } + for i := range coins { + if !coins[i].Collateral { + continue + } + f.collateralWeight.loadTotal(currency.NewCode(coins[i].ID), coins[i].CollateralWeight) + } + + futures, err := f.GetFutures(ctx) + if err != nil { + return err + } + for i := range futures { + f.collateralWeight.loadIMF(currency.NewCode(futures[i].Underlying), futures[i].IMFFactor) + } + } + + return nil +} + +func (c CollateralWeightHolder) isLoaded() bool { + return len(c) > 0 +} + +func (c CollateralWeightHolder) loadTotal(code currency.Code, weighting float64) { + butts, ok := c[code] + if !ok { + butts = CollateralWeight{Total: weighting} + } else { + butts.Total = weighting + } + c[code] = butts +} + +func (c CollateralWeightHolder) loadIMF(code currency.Code, imf float64) { + butts, ok := c[code] + if !ok { + butts = CollateralWeight{IMFFactor: imf} + } else { + butts.IMFFactor = imf + } + c[code] = butts +} + +func (c CollateralWeightHolder) load(code currency.Code, initial, total, imfFactor float64) { + c[code] = CollateralWeight{ + Initial: initial, + Total: total, + IMFFactor: imfFactor, + } +} diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index d9124935cdb..1131b71ab67 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -3,6 +3,7 @@ package ftx import ( "time" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -880,3 +881,12 @@ type StakeReward struct { Status string `json:"status"` Time time.Time `json:"time"` } + +// CollateralWeightHolder stores collateral weights over the lifecycle of the application +type CollateralWeightHolder map[currency.Code]CollateralWeight + +type CollateralWeight struct { + Initial float64 + Total float64 + IMFFactor float64 +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index c5564061185..78a94dd3c61 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -10,6 +10,7 @@ import ( "sync" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" @@ -247,6 +248,16 @@ func (f *FTX) Run() { f.Name, err) } + + if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { + err = f.StoreCollateralWeightings() + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to store collateral weightings. Err: %s", + f.Name, + err) + } + } } // FetchTradablePairs returns a list of the exchanges tradable pairs @@ -1254,3 +1265,50 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur } return availableChains, nil } + +func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, error) { + var result *exchange.PNLResult + if !pnl.CalculateOffline { + + collat, err := f.CalculateCollateral(pnl.Underlying, pnl.Amount, pnl.EntryPrice, result.UnrealisedPNL.IsPositive()) + if err != nil { + return nil, err + } + result.Collateral = decimal.NewFromFloat(collat) + return result, nil + } else { + ctx := context.Background() + info, err := f.GetAccountInfo(ctx) + if err != nil { + return nil, err + } + if info.Liquidating || info.Collateral == 0 { + result.IsLiquidated = true + return result, nil + } + for i := range info.Positions { + ftxSide := order.Side(info.Positions[i].Side) + var pnlSide order.Side + switch ftxSide { + case order.Sell: + pnlSide = order.Short + case order.Buy: + pnlSide = order.Long + default: + return nil, order.ErrSideIsInvalid + } + if info.Positions[i].EntryPrice == pnl.EntryPrice && pnl.Side == pnlSide { + result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPnL) + result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPnL) + var collat float64 + collat, err = f.CalculateCollateral(pnl.Underlying, info.Positions[i].Size, info.Positions[i].EntryPrice, result.UnrealisedPNL.IsPositive()) + if err != nil { + return nil, err + } + result.Collateral = decimal.NewFromFloat(collat) + return result, nil + } + } + } + return nil, nil +} diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index c3519379fbd..c0b7960e675 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -714,6 +714,10 @@ func StringToOrderSide(side string) (Side, error) { return Bid, nil case strings.EqualFold(side, Ask.String()): return Ask, nil + case strings.EqualFold(side, Long.String()): + return Long, nil + case strings.EqualFold(side, Short.String()): + return Short, nil case strings.EqualFold(side, AnySide.String()): return AnySide, nil default: From 889d388e63f6af96bc9186d4f13b9f888fdfe9c4 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 29 Nov 2021 16:27:35 +1100 Subject: [PATCH 030/171] Further FTX collateral workings --- backtester/backtest/backtest.go | 23 +- .../portfolio/holdings/holdings.go | 24 +- .../eventhandlers/portfolio/portfolio.go | 69 ++-- backtester/funding/collateral.go | 4 + backtester/funding/funding_types.go | 1 + exchanges/exchange.go | 25 +- exchanges/ftx/ftx.go | 297 +++++++++--------- exchanges/ftx/ftx_wrapper.go | 65 ++-- 8 files changed, 277 insertions(+), 231 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index b7c671a65be..f3b1f7a0d29 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -38,6 +38,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/report" gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" + gctconfig "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" gctdatabase "github.com/thrasher-corp/gocryptotrader/database" "github.com/thrasher-corp/gocryptotrader/engine" @@ -156,10 +157,19 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, if err != nil { return nil, err } - _, err = exch.GetDefaultConfig() + var conf *gctconfig.Exchange + conf, err = exch.GetDefaultConfig() if err != nil { return nil, err } + conf.Enabled = true + // because for some reason we're supposed to set this + // even if no websocket is enabled + err = exch.Setup(conf) + if err != nil { + return nil, err + } + exchBase := exch.GetBase() err = exch.UpdateTradablePairs(context.Background(), true) if err != nil { @@ -1109,10 +1119,13 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if err != nil { log.Error(log.BackTester, err) } - - err = bt.Statistic.AddHoldingsForTime(holding) - if err != nil { - log.Error(log.BackTester, err) + if holding == nil { + log.Error(log.BackTester, "why is holdings nill?") + } else { + err = bt.Statistic.AddHoldingsForTime(holding) + if err != nil { + log.Error(log.BackTester, err) + } } var cp *compliance.Manager diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index c5d3d676095..01676c16439 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -1,6 +1,8 @@ package holdings import ( + "fmt" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" @@ -14,9 +16,25 @@ func Create(ev ClosePriceReader, fundReader funding.IFundReader) (Holding, error if ev == nil { return Holding{}, common.ErrNilEvent } - if ev.GetAssetType().IsFutures() { - } else { + if ev.GetAssetType().IsFutures() { + funds, err := fundReader.GetCollateralReader() + if err != nil { + return Holding{}, err + } + return Holding{ + Offset: ev.GetOffset(), + Pair: ev.Pair(), + Asset: ev.GetAssetType(), + Exchange: ev.GetExchange(), + Timestamp: ev.GetTime(), + QuoteInitialFunds: funds.InitialFunds(), + QuoteSize: funds.InitialFunds(), + BaseInitialFunds: decimal.Zero, + BaseSize: decimal.Zero, + TotalInitialValue: funds.InitialFunds(), + }, nil + } else if ev.GetAssetType() == asset.Spot { funds, err := fundReader.GetPairReader() if err != nil { return Holding{}, err @@ -38,7 +56,7 @@ func Create(ev ClosePriceReader, fundReader funding.IFundReader) (Holding, error TotalInitialValue: funds.QuoteInitialFunds().Add(funds.BaseInitialFunds().Mul(ev.GetClosePrice())), }, nil } - return Holding{}, asset.ErrNotSupported + return Holding{}, fmt.Errorf("%v %w", ev.GetAssetType(), asset.ErrNotSupported) } // Update calculates holding statistics for the events time diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b22ce4bbd5d..5c44a878103 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -233,32 +233,33 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. } } - fp, err := funding.GetPairReleaser() - if err != nil { - return nil, err - } - - // Get the holding from the previous iteration, create it if it doesn't yet have a timestamp - h := lookup.GetHoldingsForTime(ev.GetTime().Add(-ev.GetInterval().Duration())) - if !h.Timestamp.IsZero() { - h.Update(ev, fp) - } else { - h = lookup.GetLatestHoldings() - if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, funding) - if err != nil { - return nil, err - } - } else { + if ev.GetAssetType() == asset.Spot { + fp, err := funding.GetPairReleaser() + if err != nil { + return nil, err + } + // Get the holding from the previous iteration, create it if it doesn't yet have a timestamp + h := lookup.GetHoldingsForTime(ev.GetTime().Add(-ev.GetInterval().Duration())) + if !h.Timestamp.IsZero() { h.Update(ev, fp) + } else { + h = lookup.GetLatestHoldings() + if h.Timestamp.IsZero() { + h, err = holdings.Create(ev, funding) + if err != nil { + return nil, err + } + } else { + h.Update(ev, fp) + } + } + err = p.setHoldingsForOffset(&h, true) + if errors.Is(err, errNoHoldings) { + err = p.setHoldingsForOffset(&h, false) + } + if err != nil { + log.Error(log.BackTester, err) } - } - err = p.setHoldingsForOffset(&h, true) - if errors.Is(err, errNoHoldings) { - err = p.setHoldingsForOffset(&h, false) - } - if err != nil { - log.Error(log.BackTester, err) } err = p.addComplianceSnapshot(ev) @@ -565,12 +566,14 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla } result, err := settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ - Asset: e.GetAssetType(), - Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, - EntryPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, - OpeningAmount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, - CurrentPrice: e.GetClosePrice().InexactFloat64(), - Collateral: funds.AvailableFunds(), + Asset: e.GetAssetType(), + Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, + EntryPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, + OpeningAmount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, + CurrentPrice: e.GetClosePrice().InexactFloat64(), + CollateralAmount: funds.AvailableFunds(), + CalculateOffline: true, + CollateralCurrency: funds.CollateralCurrency(), }) if err != nil { return err @@ -585,11 +588,11 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla return nil } - snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.PNL - snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = result.PNL + snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.UnrealisedPNL + snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = result.UnrealisedPNL snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ Time: e.GetTime(), - UnrealisedPNL: result.PNL, + UnrealisedPNL: result.UnrealisedPNL, }) } return nil diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 642de4090f7..fcfc93fb288 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -87,3 +87,7 @@ func (c *Collateral) Liquidate() { c.Collateral.available = decimal.Zero c.Contract.available = decimal.Zero } + +func (c *Collateral) CurrentHoldings() decimal.Decimal { + return c.Contract.available +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 440daa14908..84656557e7b 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -90,6 +90,7 @@ type ICollateralReader interface { CollateralCurrency() currency.Code InitialFunds() decimal.Decimal AvailableFunds() decimal.Decimal + CurrentHoldings() decimal.Decimal } // IPairReleaser limits funding usage for exchange event handling diff --git a/exchanges/exchange.go b/exchanges/exchange.go index bf67c0c9843..f8d173de45d 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1463,17 +1463,18 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ } type PNLCalculator struct { - CalculateOffline bool - Underlying currency.Code - OrderID string - Asset asset.Item - Side order.Side - Leverage float64 - EntryPrice float64 - OpeningAmount float64 - Amount float64 - CurrentPrice float64 - Collateral decimal.Decimal + CalculateOffline bool + Underlying currency.Code + OrderID string + Asset asset.Item + Side order.Side + Leverage float64 + EntryPrice float64 + OpeningAmount float64 + Amount float64 + CurrentPrice float64 + CollateralAmount decimal.Decimal + CollateralCurrency currency.Code } type PNLResult struct { @@ -1505,7 +1506,7 @@ func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { op := decimal.NewFromFloat(calc.EntryPrice) lv := decimal.NewFromFloat(calc.Leverage) result.UnrealisedPNL = cp.Sub(op).Mul(op).Mul(lv) - if result.UnrealisedPNL.IsNegative() && calc.Collateral.LessThanOrEqual(result.UnrealisedPNL.Abs()) { + if result.UnrealisedPNL.IsNegative() && calc.CollateralAmount.LessThanOrEqual(result.UnrealisedPNL.Abs()) { // calculating whether something is liquidated changes per exchange // If your chosen exchange has its own liquidation formula, please ensure // it is implemented there rather than rely on this base function diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index d5d2344ab12..db21e330e3f 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1495,158 +1495,159 @@ func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64 // LoadCollateralWeightings sets the collateral weights for // currencies supported by FTX -func (f *FTX) LoadCollateralWeightings(useOfflineWeightings bool) error { +func (f *FTX) LoadCollateralWeightings() error { f.collateralWeight = make(map[currency.Code]CollateralWeight) + // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral + // sets default, then uses the latest from FTX + f.collateralWeight.load(currency.NewCode("1INCH"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("AAPL"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("AAVE"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("ABNB"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("ACB"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("ALPHA"), 0.9, 0.85, 0.00025) + f.collateralWeight.load(currency.NewCode("AMC"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("AMD"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("AMZN"), 0.9, 0.85, 0.03) + f.collateralWeight.load(currency.NewCode("APHA"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("ARKK"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("AUD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("BABA"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("BADGER"), 0.85, 0.8, 0.0025) + f.collateralWeight.load(currency.NewCode("BAND"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("BAO"), 0.85, 0.8, 0.000025) + f.collateralWeight.load(currency.NewCode("BB"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("BCH"), 0.95, 0.9, 0.0008) + f.collateralWeight.load(currency.NewCode("BILI"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("BITW"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("BNB"), 0.95, 0.9, 0.0005) + f.collateralWeight.load(currency.NewCode("BNT"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("BNTX"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("BRL"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("BRZ"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("BTC"), 0.975, 0.95, 0.002) + f.collateralWeight.load(currency.NewCode("BTMX"), 0.7, 0.65, 0.0008) + f.collateralWeight.load(currency.NewCode("BUSD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("BVOL"), 0.85, 0.8, 0.005) + f.collateralWeight.load(currency.NewCode("BYND"), 0.9, 0.85, 0.0075) + f.collateralWeight.load(currency.NewCode("CAD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("CEL"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("CGC"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("CHF"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("COIN"), 0.85, 0.8, 0.01) + f.collateralWeight.load(currency.NewCode("COMP"), 0.9, 0.85, 0.002) + f.collateralWeight.load(currency.NewCode("COPE"), 0.6, 0.55, 0.02) + f.collateralWeight.load(currency.NewCode("CRON"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("CUSDT"), 0.9, 0.85, 0.00001) + f.collateralWeight.load(currency.NewCode("DAI"), 0.9, 0.85, 0.00005) + f.collateralWeight.load(currency.NewCode("DOGE"), 0.95, 0.9, 0.00002) + f.collateralWeight.load(currency.NewCode("ETH"), 0.95, 0.9, 0.0004) + f.collateralWeight.load(currency.NewCode("STETH"), 0.9, 0.85, 0.0012) + f.collateralWeight.load(currency.NewCode("ETHE"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("EUR"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("FB"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("FIDA"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("FTM"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("FTT"), 0.95, 0.95, 0.0005) + f.collateralWeight.load(currency.NewCode("GBP"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("GBTC"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("GDX"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("GDXJ"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GLD"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GLXY"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GME"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("GOOGL"), 0.9, 0.85, 0.025) + f.collateralWeight.load(currency.NewCode("GRT"), 0.9, 0.85, 0.00025) + f.collateralWeight.load(currency.NewCode("HKD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("HOLY"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("HOOD"), 0.85, 0.8, 0.005) + f.collateralWeight.load(currency.NewCode("HT"), 0.9, 0.85, 0.0003) + f.collateralWeight.load(currency.NewCode("HUSD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("HXRO"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("IBVOL"), 0.85, 0.8, 0.015) + f.collateralWeight.load(currency.NewCode("KIN"), 0.85, 0.8, 0.000008) + f.collateralWeight.load(currency.NewCode("KNC"), 0.95, 0.9, 0.001) + f.collateralWeight.load(currency.NewCode("LEO"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("LINK"), 0.95, 0.9, 0.0004) + f.collateralWeight.load(currency.NewCode("LRC"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("LTC"), 0.95, 0.9, 0.0004) + f.collateralWeight.load(currency.NewCode("MATIC"), 0.85, 0.8, 0.00004) + f.collateralWeight.load(currency.NewCode("MKR"), 0.9, 0.85, 0.007) + f.collateralWeight.load(currency.NewCode("MOB"), 0.6, 0.55, 0.005) + f.collateralWeight.load(currency.NewCode("MRNA"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("MSTR"), 0.9, 0.85, 0.008) + f.collateralWeight.load(currency.NewCode("NFLX"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("NIO"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("NOK"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("NVDA"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("OKB"), 0.9, 0.85, 0.0003) + f.collateralWeight.load(currency.NewCode("OMG"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("USDP"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("PAXG"), 0.95, 0.9, 0.002) + f.collateralWeight.load(currency.NewCode("PENN"), 0.9, 0.85, 0.005) + f.collateralWeight.load(currency.NewCode("PFE"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("PYPL"), 0.9, 0.85, 0.008) + f.collateralWeight.load(currency.NewCode("RAY"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("REN"), 0.9, 0.85, 0.00025) + f.collateralWeight.load(currency.NewCode("RSR"), 0.85, 0.8, 0.0001) + f.collateralWeight.load(currency.NewCode("RUNE"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("SECO"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("SGD"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("SLV"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("SNX"), 0.85, 0.8, 0.001) + f.collateralWeight.load(currency.NewCode("SOL"), 0.9, 0.85, 0.0004) + f.collateralWeight.load(currency.NewCode("STSOL"), 0.9, 0.85, 0.0004) + f.collateralWeight.load(currency.NewCode("MSOL"), 0.9, 0.85, 0.0004) + f.collateralWeight.load(currency.NewCode("SPY"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("SQ"), 0.9, 0.85, 0.008) + f.collateralWeight.load(currency.NewCode("SRM"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("SUSHI"), 0.95, 0.9, 0.001) + f.collateralWeight.load(currency.NewCode("SXP"), 0.9, 0.85, 0.0005) + f.collateralWeight.load(currency.NewCode("TLRY"), 0.9, 0.85, 0.001) + f.collateralWeight.load(currency.NewCode("TOMO"), 0.85, 0.8, 0.0005) + f.collateralWeight.load(currency.NewCode("TRX"), 0.9, 0.85, 0.00001) + f.collateralWeight.load(currency.NewCode("TRY"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("TRYB"), 0.9, 0.85, 0.00001) + f.collateralWeight.load(currency.NewCode("TSLA"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("TSM"), 0.9, 0.85, 0.015) + f.collateralWeight.load(currency.NewCode("TUSD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("TWTR"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("UBER"), 0.9, 0.85, 0.004) + f.collateralWeight.load(currency.NewCode("UNI"), 0.95, 0.9, 0.001) + f.collateralWeight.load(currency.NewCode("USD"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("USDC"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("USDT"), 0.975, 0.95, 0.00001) + f.collateralWeight.load(currency.NewCode("USO"), 0.9, 0.85, 0.0025) + f.collateralWeight.load(currency.NewCode("WBTC"), 0.975, 0.95, 0.005) + f.collateralWeight.load(currency.NewCode("WUSDC"), 1, 1, 0) + f.collateralWeight.load(currency.NewCode("WUSDT"), 0.975, 0.95, 0.00001) + f.collateralWeight.load(currency.NewCode("XAUT"), 0.95, 0.9, 0.002) + f.collateralWeight.load(currency.NewCode("XRP"), 0.95, 0.9, 0.00002) + f.collateralWeight.load(currency.NewCode("YFI"), 0.9, 0.85, 0.015) + f.collateralWeight.load(currency.NewCode("ZAR"), 0.99, 0.98, 0.00001) + f.collateralWeight.load(currency.NewCode("ZM"), 0.9, 0.85, 0.01) + f.collateralWeight.load(currency.NewCode("ZRX"), 0.85, 0.8, 0.001) + + if !f.GetAuthenticatedAPISupport(exchange.RestAuthentication) { + return nil + } ctx := context.Background() - if useOfflineWeightings { - // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral - // not sure if ill keep this - f.collateralWeight.load(currency.NewCode("1INCH"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("AAPL"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("AAVE"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("ABNB"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("ACB"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("ALPHA"), 0.9, 0.85, 0.00025) - f.collateralWeight.load(currency.NewCode("AMC"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("AMD"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("AMZN"), 0.9, 0.85, 0.03) - f.collateralWeight.load(currency.NewCode("APHA"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("ARKK"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("AUD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("BABA"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("BADGER"), 0.85, 0.8, 0.0025) - f.collateralWeight.load(currency.NewCode("BAND"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("BAO"), 0.85, 0.8, 0.000025) - f.collateralWeight.load(currency.NewCode("BB"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("BCH"), 0.95, 0.9, 0.0008) - f.collateralWeight.load(currency.NewCode("BILI"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("BITW"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("BNB"), 0.95, 0.9, 0.0005) - f.collateralWeight.load(currency.NewCode("BNT"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("BNTX"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("BRL"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("BRZ"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("BTC"), 0.975, 0.95, 0.002) - f.collateralWeight.load(currency.NewCode("BTMX"), 0.7, 0.65, 0.0008) - f.collateralWeight.load(currency.NewCode("BUSD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("BVOL"), 0.85, 0.8, 0.005) - f.collateralWeight.load(currency.NewCode("BYND"), 0.9, 0.85, 0.0075) - f.collateralWeight.load(currency.NewCode("CAD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("CEL"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("CGC"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("CHF"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("COIN"), 0.85, 0.8, 0.01) - f.collateralWeight.load(currency.NewCode("COMP"), 0.9, 0.85, 0.002) - f.collateralWeight.load(currency.NewCode("COPE"), 0.6, 0.55, 0.02) - f.collateralWeight.load(currency.NewCode("CRON"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("CUSDT"), 0.9, 0.85, 0.00001) - f.collateralWeight.load(currency.NewCode("DAI"), 0.9, 0.85, 0.00005) - f.collateralWeight.load(currency.NewCode("DOGE"), 0.95, 0.9, 0.00002) - f.collateralWeight.load(currency.NewCode("ETH"), 0.95, 0.9, 0.0004) - f.collateralWeight.load(currency.NewCode("STETH"), 0.9, 0.85, 0.0012) - f.collateralWeight.load(currency.NewCode("ETHE"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("EUR"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("FB"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("FIDA"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("FTM"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("FTT"), 0.95, 0.95, 0.0005) - f.collateralWeight.load(currency.NewCode("GBP"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("GBTC"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("GDX"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("GDXJ"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GLD"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GLXY"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GME"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GOOGL"), 0.9, 0.85, 0.025) - f.collateralWeight.load(currency.NewCode("GRT"), 0.9, 0.85, 0.00025) - f.collateralWeight.load(currency.NewCode("HKD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("HOLY"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("HOOD"), 0.85, 0.8, 0.005) - f.collateralWeight.load(currency.NewCode("HT"), 0.9, 0.85, 0.0003) - f.collateralWeight.load(currency.NewCode("HUSD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("HXRO"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("IBVOL"), 0.85, 0.8, 0.015) - f.collateralWeight.load(currency.NewCode("KIN"), 0.85, 0.8, 0.000008) - f.collateralWeight.load(currency.NewCode("KNC"), 0.95, 0.9, 0.001) - f.collateralWeight.load(currency.NewCode("LEO"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("LINK"), 0.95, 0.9, 0.0004) - f.collateralWeight.load(currency.NewCode("LRC"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("LTC"), 0.95, 0.9, 0.0004) - f.collateralWeight.load(currency.NewCode("MATIC"), 0.85, 0.8, 0.00004) - f.collateralWeight.load(currency.NewCode("MKR"), 0.9, 0.85, 0.007) - f.collateralWeight.load(currency.NewCode("MOB"), 0.6, 0.55, 0.005) - f.collateralWeight.load(currency.NewCode("MRNA"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("MSTR"), 0.9, 0.85, 0.008) - f.collateralWeight.load(currency.NewCode("NFLX"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("NIO"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("NOK"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("NVDA"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("OKB"), 0.9, 0.85, 0.0003) - f.collateralWeight.load(currency.NewCode("OMG"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("USDP"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("PAXG"), 0.95, 0.9, 0.002) - f.collateralWeight.load(currency.NewCode("PENN"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("PFE"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("PYPL"), 0.9, 0.85, 0.008) - f.collateralWeight.load(currency.NewCode("RAY"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("REN"), 0.9, 0.85, 0.00025) - f.collateralWeight.load(currency.NewCode("RSR"), 0.85, 0.8, 0.0001) - f.collateralWeight.load(currency.NewCode("RUNE"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("SECO"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("SGD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("SLV"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("SNX"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("SOL"), 0.9, 0.85, 0.0004) - f.collateralWeight.load(currency.NewCode("STSOL"), 0.9, 0.85, 0.0004) - f.collateralWeight.load(currency.NewCode("MSOL"), 0.9, 0.85, 0.0004) - f.collateralWeight.load(currency.NewCode("SPY"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("SQ"), 0.9, 0.85, 0.008) - f.collateralWeight.load(currency.NewCode("SRM"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("SUSHI"), 0.95, 0.9, 0.001) - f.collateralWeight.load(currency.NewCode("SXP"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("TLRY"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("TOMO"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("TRX"), 0.9, 0.85, 0.00001) - f.collateralWeight.load(currency.NewCode("TRY"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("TRYB"), 0.9, 0.85, 0.00001) - f.collateralWeight.load(currency.NewCode("TSLA"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("TSM"), 0.9, 0.85, 0.015) - f.collateralWeight.load(currency.NewCode("TUSD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("TWTR"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("UBER"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("UNI"), 0.95, 0.9, 0.001) - f.collateralWeight.load(currency.NewCode("USD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("USDC"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("USDT"), 0.975, 0.95, 0.00001) - f.collateralWeight.load(currency.NewCode("USO"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("WBTC"), 0.975, 0.95, 0.005) - f.collateralWeight.load(currency.NewCode("WUSDC"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("WUSDT"), 0.975, 0.95, 0.00001) - f.collateralWeight.load(currency.NewCode("XAUT"), 0.95, 0.9, 0.002) - f.collateralWeight.load(currency.NewCode("XRP"), 0.95, 0.9, 0.00002) - f.collateralWeight.load(currency.NewCode("YFI"), 0.9, 0.85, 0.015) - f.collateralWeight.load(currency.NewCode("ZAR"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("ZM"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("ZRX"), 0.85, 0.8, 0.001) - } else { - coins, err := f.GetCoins(ctx) - if err != nil { - return err - } - for i := range coins { - if !coins[i].Collateral { - continue - } - f.collateralWeight.loadTotal(currency.NewCode(coins[i].ID), coins[i].CollateralWeight) + coins, err := f.GetCoins(ctx) + if err != nil { + return err + } + for i := range coins { + if !coins[i].Collateral { + continue } + f.collateralWeight.loadTotal(currency.NewCode(coins[i].ID), coins[i].CollateralWeight) + } - futures, err := f.GetFutures(ctx) - if err != nil { - return err - } - for i := range futures { - f.collateralWeight.loadIMF(currency.NewCode(futures[i].Underlying), futures[i].IMFFactor) - } + futures, err := f.GetFutures(ctx) + if err != nil { + return err + } + for i := range futures { + f.collateralWeight.loadIMF(currency.NewCode(futures[i].Underlying), futures[i].IMFFactor) } return nil diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 78a94dd3c61..5070e9c663f 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -185,25 +185,40 @@ func (f *FTX) Setup(exch *config.Exchange) error { return err } - err = f.Websocket.Setup(&stream.WebsocketSetup{ - ExchangeConfig: exch, - DefaultURL: ftxWSURL, - RunningURL: wsEndpoint, - Connector: f.WsConnect, - Subscriber: f.Subscribe, - Unsubscriber: f.Unsubscribe, - GenerateSubscriptions: f.GenerateDefaultSubscriptions, - Features: &f.Features.Supports.WebsocketCapabilities, - TradeFeed: f.Features.Enabled.TradeFeed, - FillsFeed: f.Features.Enabled.FillsFeed, - }) - if err != nil { - return err + if exch.Websocket != nil && *exch.Websocket { + err = f.Websocket.Setup(&stream.WebsocketSetup{ + ExchangeConfig: exch, + DefaultURL: ftxWSURL, + RunningURL: wsEndpoint, + Connector: f.WsConnect, + Subscriber: f.Subscribe, + Unsubscriber: f.Unsubscribe, + GenerateSubscriptions: f.GenerateDefaultSubscriptions, + Features: &f.Features.Supports.WebsocketCapabilities, + TradeFeed: f.Features.Enabled.TradeFeed, + FillsFeed: f.Features.Enabled.FillsFeed, + }) + if err != nil { + return err + } } - return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - }) + + if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { + err = f.LoadCollateralWeightings() + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to store collateral weightings. Err: %s", + f.Name, + err) + } + } + if exch.Websocket != nil && *exch.Websocket { + return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + }) + } + return nil } // Start starts the FTX go routine @@ -249,15 +264,6 @@ func (f *FTX) Run() { err) } - if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { - err = f.StoreCollateralWeightings() - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to store collateral weightings. Err: %s", - f.Name, - err) - } - } } // FetchTradablePairs returns a list of the exchanges tradable pairs @@ -1268,9 +1274,8 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, error) { var result *exchange.PNLResult - if !pnl.CalculateOffline { - - collat, err := f.CalculateCollateral(pnl.Underlying, pnl.Amount, pnl.EntryPrice, result.UnrealisedPNL.IsPositive()) + if pnl.CalculateOffline { + collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.Amount, pnl.EntryPrice, true) if err != nil { return nil, err } From 5e72026fa84d068b07a747c8493d558f6a37a58a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 30 Nov 2021 16:53:54 +1100 Subject: [PATCH 031/171] hooks up more ftx collateral and pnl calculations --- backtester/backtest/backtest.go | 4 +- backtester/common/common_types.go | 6 + backtester/config/config_test.go | 4 +- backtester/config/config_types.go | 7 +- .../config/examples/futures-api-candles.strat | 16 +- backtester/data/kline/kline.go | 3 +- backtester/data/kline/kline_types.go | 2 + .../eventhandlers/portfolio/portfolio.go | 11 +- backtester/eventtypes/kline/kline.go | 22 +- backtester/eventtypes/kline/kline_types.go | 9 + backtester/report/tpl.gohtml | 2 +- exchanges/exchange.go | 2 + exchanges/ftx/ftx.go | 270 +++++++++--------- exchanges/ftx/ftx_types.go | 3 +- exchanges/ftx/ftx_wrapper.go | 16 +- exchanges/order/order_types.go | 1 + 16 files changed, 215 insertions(+), 163 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index f3b1f7a0d29..1446e189743 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -253,7 +253,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, } if cfg.CurrencySettings[i].FuturesDetails != nil { portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio - portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumLeverageRate + portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate } portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet if cfg.CurrencySettings[i].MakerFee.GreaterThan(cfg.CurrencySettings[i].TakerFee) { @@ -550,7 +550,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange if cfg.CurrencySettings[i].FuturesDetails != nil { lev = exchange.Leverage{ CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, - MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumLeverageRate, + MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, } } diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 75071005c13..72e6f95ef6c 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -75,6 +75,12 @@ type DataEventHandler interface { GetHighPrice() decimal.Decimal GetLowPrice() decimal.Decimal GetOpenPrice() decimal.Decimal + GetFuturesDataEventHandler() (FuturesDataEventHandler, error) +} + +type FuturesDataEventHandler interface { + GetMarkPrice() decimal.Decimal + GetPreviousMarkPrice() decimal.Decimal } // Directioner dictates the side of an order diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 0d8ccb54416..d296bba6218 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1227,8 +1227,8 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { FuturesDetails: &FuturesDetails{ CollateralCurrency: "USDT", Leverage: Leverage{ - CanUseLeverage: true, - MaximumLeverageRate: 1, + CanUseLeverage: true, + MaximumOrderLeverageRate: 1, }, }, BuySide: minMax, diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 4ec14ceb6de..16bfe3d6f0b 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -99,7 +99,12 @@ type PortfolioSettings struct { type Leverage struct { CanUseLeverage bool `json:"can-use-leverage"` MaximumOrdersWithLeverageRatio decimal.Decimal `json:"maximum-orders-with-leverage-ratio"` - MaximumLeverageRate float64 `json:"maximum-leverage-rate"` + // this means you can place an order with higher leverage rate. eg have $100 in collateral, + // but place an order for $200 using 2x leverage + MaximumOrderLeverageRate float64 `json:"maximum-leverage-rate"` + // this means you can place orders at `1x leverage, but utilise collateral as leverage to place more. + // eg if this is 2x, and collateral is $100 I can place two long/shorts of $100 + MaximumCollateralLeverageRate float64 `json:"maximum-collateral-leverage-rate"` } // MinMax are the rules which limit the placement of orders. diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/futures-api-candles.strat index 8917307b707..3a246a24784 100644 --- a/backtester/config/examples/futures-api-candles.strat +++ b/backtester/config/examples/futures-api-candles.strat @@ -32,14 +32,10 @@ "collateral-currency": "USDT" }, "buy-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "maximum-total": "1000" }, "sell-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "maximum-total": "1000" }, "min-slippage-percent": "0", "max-slippage-percent": "0", @@ -66,14 +62,10 @@ "maximum-leverage-rate": 0 }, "buy-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "maximum-total": "1000" }, "sell-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "maximum-total": "1000" } }, "statistic-settings": { diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index 9adcebc1b4c..7cde8784a14 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -29,7 +29,7 @@ func (d *DataFromKline) Load() error { klineData := make([]common.DataEventHandler, len(d.Item.Candles)) for i := range d.Item.Candles { - klineData[i] = &kline.Kline{ + klinerino := &kline.Kline{ Base: event.Base{ Offset: int64(i + 1), Exchange: d.Item.Exchange, @@ -45,6 +45,7 @@ func (d *DataFromKline) Load() error { Volume: decimal.NewFromFloat(d.Item.Candles[i].Volume), ValidationIssues: d.Item.Candles[i].ValidationIssues, } + klineData[i] = klinerino d.addedTimes[d.Item.Candles[i].Time] = true } diff --git a/backtester/data/kline/kline_types.go b/backtester/data/kline/kline_types.go index d44d395f51c..46fc596a7e8 100644 --- a/backtester/data/kline/kline_types.go +++ b/backtester/data/kline/kline_types.go @@ -5,6 +5,7 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) @@ -16,5 +17,6 @@ type DataFromKline struct { data.Base addedTimes map[time.Time]bool Item gctkline.Item + FuturesData []kline.FuturesData RangeHolder *gctkline.IntervalRangeHolder } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 5c44a878103..0d0455c1d3d 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -550,6 +550,7 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla if !ok { return errNoPortfolioSettings } + snapshot, err := p.GetLatestOrderSnapshotForEvent(e) if err != nil { return err @@ -565,7 +566,8 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 } - result, err := settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ + var result *gctexchange.PNLResult + result, err = settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ Asset: e.GetAssetType(), Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, EntryPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, @@ -574,10 +576,14 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla CollateralAmount: funds.AvailableFunds(), CalculateOffline: true, CollateralCurrency: funds.CollateralCurrency(), + Amount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, + MarkPrice: e.GetClosePrice().InexactFloat64(), + PrevMarkPrice: e.GetOpenPrice().InexactFloat64(), }) if err != nil { return err } + if result.IsLiquidated { funds.Liquidate() snapshot.Orders[i].FuturesOrder.UnrealisedPNL = decimal.Zero @@ -587,12 +593,13 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla snapshot.Orders[i].FuturesOrder.ClosingPosition = &or return nil } - + snapshot.Orders[i].FuturesOrder.RealisedPNL.Add(snapshot.Orders[i].FuturesOrder.UnrealisedPNL) snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.UnrealisedPNL snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = result.UnrealisedPNL snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ Time: e.GetTime(), UnrealisedPNL: result.UnrealisedPNL, + RealisedPNL: snapshot.Orders[i].FuturesOrder.RealisedPNL, }) } return nil diff --git a/backtester/eventtypes/kline/kline.go b/backtester/eventtypes/kline/kline.go index d215a596d92..0a689ba3d3a 100644 --- a/backtester/eventtypes/kline/kline.go +++ b/backtester/eventtypes/kline/kline.go @@ -1,6 +1,11 @@ package kline -import "github.com/shopspring/decimal" +import ( + "fmt" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" +) // GetClosePrice returns the closing price of a kline func (k *Kline) GetClosePrice() decimal.Decimal { @@ -21,3 +26,18 @@ func (k *Kline) GetLowPrice() decimal.Decimal { func (k *Kline) GetOpenPrice() decimal.Decimal { return k.Open } + +func (k *Kline) GetFuturesDataEventHandler() (common.FuturesDataEventHandler, error) { + if !k.AssetType.IsFutures() { + return nil, fmt.Errorf("not futures") + } + return k.FuturesData, nil +} + +func (f *FuturesData) GetMarkPrice() decimal.Decimal { + return f.MarkPrice +} + +func (f *FuturesData) GetPreviousMarkPrice() decimal.Decimal { + return f.PrevMarkPrice +} diff --git a/backtester/eventtypes/kline/kline_types.go b/backtester/eventtypes/kline/kline_types.go index 71121e5b080..6e4a5e5ecbd 100644 --- a/backtester/eventtypes/kline/kline_types.go +++ b/backtester/eventtypes/kline/kline_types.go @@ -1,6 +1,8 @@ package kline import ( + "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" ) @@ -15,4 +17,11 @@ type Kline struct { High decimal.Decimal Volume decimal.Decimal ValidationIssues string + FuturesData *FuturesData +} + +type FuturesData struct { + Time time.Time + MarkPrice decimal.Decimal + PrevMarkPrice decimal.Decimal } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 94dadceb351..85950fe9526 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -471,7 +471,7 @@ {{.Config.PortfolioSettings.Leverage.CanUseLeverage}} - {{.Config.PortfolioSettings.Leverage.MaximumLeverageRate}} + {{.Config.PortfolioSettings.Leverage.MaximumOrderLeverageRate}} {{.Config.PortfolioSettings.Leverage.MaximumOrdersWithLeverageRatio}} {{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MinimumSize}} {{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumSize}} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index f8d173de45d..62f5b229240 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1472,6 +1472,8 @@ type PNLCalculator struct { EntryPrice float64 OpeningAmount float64 Amount float64 + MarkPrice float64 + PrevMarkPrice float64 CurrentPrice float64 CollateralAmount decimal.Decimal CollateralCurrency currency.Code diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index db21e330e3f..084194a3211 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1471,7 +1471,7 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err } func (f *FTX) CalculateCollateral(code currency.Code, amount, price float64, isPositiveBalance bool) (float64, error) { - collateralWeight, ok := f.collateralWeight[code] + collateralWeight, ok := f.collateralWeight[code.Upper().String()] if !ok { return 0, errCoinMustBeSpecified } @@ -1486,7 +1486,7 @@ func (f *FTX) CalculateCollateral(code currency.Code, amount, price float64, isP } func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64, side order.Side) (float64, error) { - collateralWeight, ok := f.collateralWeight[code] + collateralWeight, ok := f.collateralWeight[code.Upper().String()] if !ok { return 0, errCoinMustBeSpecified } @@ -1496,136 +1496,136 @@ func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64 // LoadCollateralWeightings sets the collateral weights for // currencies supported by FTX func (f *FTX) LoadCollateralWeightings() error { - f.collateralWeight = make(map[currency.Code]CollateralWeight) + f.collateralWeight = make(map[string]CollateralWeight) // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral // sets default, then uses the latest from FTX - f.collateralWeight.load(currency.NewCode("1INCH"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("AAPL"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("AAVE"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("ABNB"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("ACB"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("ALPHA"), 0.9, 0.85, 0.00025) - f.collateralWeight.load(currency.NewCode("AMC"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("AMD"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("AMZN"), 0.9, 0.85, 0.03) - f.collateralWeight.load(currency.NewCode("APHA"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("ARKK"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("AUD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("BABA"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("BADGER"), 0.85, 0.8, 0.0025) - f.collateralWeight.load(currency.NewCode("BAND"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("BAO"), 0.85, 0.8, 0.000025) - f.collateralWeight.load(currency.NewCode("BB"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("BCH"), 0.95, 0.9, 0.0008) - f.collateralWeight.load(currency.NewCode("BILI"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("BITW"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("BNB"), 0.95, 0.9, 0.0005) - f.collateralWeight.load(currency.NewCode("BNT"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("BNTX"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("BRL"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("BRZ"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("BTC"), 0.975, 0.95, 0.002) - f.collateralWeight.load(currency.NewCode("BTMX"), 0.7, 0.65, 0.0008) - f.collateralWeight.load(currency.NewCode("BUSD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("BVOL"), 0.85, 0.8, 0.005) - f.collateralWeight.load(currency.NewCode("BYND"), 0.9, 0.85, 0.0075) - f.collateralWeight.load(currency.NewCode("CAD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("CEL"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("CGC"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("CHF"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("COIN"), 0.85, 0.8, 0.01) - f.collateralWeight.load(currency.NewCode("COMP"), 0.9, 0.85, 0.002) - f.collateralWeight.load(currency.NewCode("COPE"), 0.6, 0.55, 0.02) - f.collateralWeight.load(currency.NewCode("CRON"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("CUSDT"), 0.9, 0.85, 0.00001) - f.collateralWeight.load(currency.NewCode("DAI"), 0.9, 0.85, 0.00005) - f.collateralWeight.load(currency.NewCode("DOGE"), 0.95, 0.9, 0.00002) - f.collateralWeight.load(currency.NewCode("ETH"), 0.95, 0.9, 0.0004) - f.collateralWeight.load(currency.NewCode("STETH"), 0.9, 0.85, 0.0012) - f.collateralWeight.load(currency.NewCode("ETHE"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("EUR"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("FB"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("FIDA"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("FTM"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("FTT"), 0.95, 0.95, 0.0005) - f.collateralWeight.load(currency.NewCode("GBP"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("GBTC"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("GDX"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("GDXJ"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GLD"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GLXY"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GME"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("GOOGL"), 0.9, 0.85, 0.025) - f.collateralWeight.load(currency.NewCode("GRT"), 0.9, 0.85, 0.00025) - f.collateralWeight.load(currency.NewCode("HKD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("HOLY"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("HOOD"), 0.85, 0.8, 0.005) - f.collateralWeight.load(currency.NewCode("HT"), 0.9, 0.85, 0.0003) - f.collateralWeight.load(currency.NewCode("HUSD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("HXRO"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("IBVOL"), 0.85, 0.8, 0.015) - f.collateralWeight.load(currency.NewCode("KIN"), 0.85, 0.8, 0.000008) - f.collateralWeight.load(currency.NewCode("KNC"), 0.95, 0.9, 0.001) - f.collateralWeight.load(currency.NewCode("LEO"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("LINK"), 0.95, 0.9, 0.0004) - f.collateralWeight.load(currency.NewCode("LRC"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("LTC"), 0.95, 0.9, 0.0004) - f.collateralWeight.load(currency.NewCode("MATIC"), 0.85, 0.8, 0.00004) - f.collateralWeight.load(currency.NewCode("MKR"), 0.9, 0.85, 0.007) - f.collateralWeight.load(currency.NewCode("MOB"), 0.6, 0.55, 0.005) - f.collateralWeight.load(currency.NewCode("MRNA"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("MSTR"), 0.9, 0.85, 0.008) - f.collateralWeight.load(currency.NewCode("NFLX"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("NIO"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("NOK"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("NVDA"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("OKB"), 0.9, 0.85, 0.0003) - f.collateralWeight.load(currency.NewCode("OMG"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("USDP"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("PAXG"), 0.95, 0.9, 0.002) - f.collateralWeight.load(currency.NewCode("PENN"), 0.9, 0.85, 0.005) - f.collateralWeight.load(currency.NewCode("PFE"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("PYPL"), 0.9, 0.85, 0.008) - f.collateralWeight.load(currency.NewCode("RAY"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("REN"), 0.9, 0.85, 0.00025) - f.collateralWeight.load(currency.NewCode("RSR"), 0.85, 0.8, 0.0001) - f.collateralWeight.load(currency.NewCode("RUNE"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("SECO"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("SGD"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("SLV"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("SNX"), 0.85, 0.8, 0.001) - f.collateralWeight.load(currency.NewCode("SOL"), 0.9, 0.85, 0.0004) - f.collateralWeight.load(currency.NewCode("STSOL"), 0.9, 0.85, 0.0004) - f.collateralWeight.load(currency.NewCode("MSOL"), 0.9, 0.85, 0.0004) - f.collateralWeight.load(currency.NewCode("SPY"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("SQ"), 0.9, 0.85, 0.008) - f.collateralWeight.load(currency.NewCode("SRM"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("SUSHI"), 0.95, 0.9, 0.001) - f.collateralWeight.load(currency.NewCode("SXP"), 0.9, 0.85, 0.0005) - f.collateralWeight.load(currency.NewCode("TLRY"), 0.9, 0.85, 0.001) - f.collateralWeight.load(currency.NewCode("TOMO"), 0.85, 0.8, 0.0005) - f.collateralWeight.load(currency.NewCode("TRX"), 0.9, 0.85, 0.00001) - f.collateralWeight.load(currency.NewCode("TRY"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("TRYB"), 0.9, 0.85, 0.00001) - f.collateralWeight.load(currency.NewCode("TSLA"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("TSM"), 0.9, 0.85, 0.015) - f.collateralWeight.load(currency.NewCode("TUSD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("TWTR"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("UBER"), 0.9, 0.85, 0.004) - f.collateralWeight.load(currency.NewCode("UNI"), 0.95, 0.9, 0.001) - f.collateralWeight.load(currency.NewCode("USD"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("USDC"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("USDT"), 0.975, 0.95, 0.00001) - f.collateralWeight.load(currency.NewCode("USO"), 0.9, 0.85, 0.0025) - f.collateralWeight.load(currency.NewCode("WBTC"), 0.975, 0.95, 0.005) - f.collateralWeight.load(currency.NewCode("WUSDC"), 1, 1, 0) - f.collateralWeight.load(currency.NewCode("WUSDT"), 0.975, 0.95, 0.00001) - f.collateralWeight.load(currency.NewCode("XAUT"), 0.95, 0.9, 0.002) - f.collateralWeight.load(currency.NewCode("XRP"), 0.95, 0.9, 0.00002) - f.collateralWeight.load(currency.NewCode("YFI"), 0.9, 0.85, 0.015) - f.collateralWeight.load(currency.NewCode("ZAR"), 0.99, 0.98, 0.00001) - f.collateralWeight.load(currency.NewCode("ZM"), 0.9, 0.85, 0.01) - f.collateralWeight.load(currency.NewCode("ZRX"), 0.85, 0.8, 0.001) + f.collateralWeight.load("1INCH", 0.9, 0.85, 0.0005) + f.collateralWeight.load("AAPL", 0.9, 0.85, 0.005) + f.collateralWeight.load("AAVE", 0.9, 0.85, 0.0025) + f.collateralWeight.load("ABNB", 0.9, 0.85, 0.005) + f.collateralWeight.load("ACB", 0.9, 0.85, 0.0025) + f.collateralWeight.load("ALPHA", 0.9, 0.85, 0.00025) + f.collateralWeight.load("AMC", 0.9, 0.85, 0.0025) + f.collateralWeight.load("AMD", 0.9, 0.85, 0.005) + f.collateralWeight.load("AMZN", 0.9, 0.85, 0.03) + f.collateralWeight.load("APHA", 0.9, 0.85, 0.001) + f.collateralWeight.load("ARKK", 0.9, 0.85, 0.005) + f.collateralWeight.load("AUD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("BABA", 0.9, 0.85, 0.01) + f.collateralWeight.load("BADGER", 0.85, 0.8, 0.0025) + f.collateralWeight.load("BAND", 0.85, 0.8, 0.001) + f.collateralWeight.load("BAO", 0.85, 0.8, 0.000025) + f.collateralWeight.load("BB", 0.9, 0.85, 0.0025) + f.collateralWeight.load("BCH", 0.95, 0.9, 0.0008) + f.collateralWeight.load("BILI", 0.9, 0.85, 0.005) + f.collateralWeight.load("BITW", 0.9, 0.85, 0.005) + f.collateralWeight.load("BNB", 0.95, 0.9, 0.0005) + f.collateralWeight.load("BNT", 0.9, 0.85, 0.0025) + f.collateralWeight.load("BNTX", 0.9, 0.85, 0.005) + f.collateralWeight.load("BRL", 0.99, 0.98, 0.00001) + f.collateralWeight.load("BRZ", 0.99, 0.98, 0.00001) + f.collateralWeight.load("BTC", 0.975, 0.95, 0.002) + f.collateralWeight.load("BTMX", 0.7, 0.65, 0.0008) + f.collateralWeight.load("BUSD", 1, 1, 0) + f.collateralWeight.load("BVOL", 0.85, 0.8, 0.005) + f.collateralWeight.load("BYND", 0.9, 0.85, 0.0075) + f.collateralWeight.load("CAD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("CEL", 0.85, 0.8, 0.001) + f.collateralWeight.load("CGC", 0.9, 0.85, 0.0025) + f.collateralWeight.load("CHF", 0.99, 0.98, 0.00001) + f.collateralWeight.load("COIN", 0.85, 0.8, 0.01) + f.collateralWeight.load("COMP", 0.9, 0.85, 0.002) + f.collateralWeight.load("COPE", 0.6, 0.55, 0.02) + f.collateralWeight.load("CRON", 0.9, 0.85, 0.001) + f.collateralWeight.load("CUSDT", 0.9, 0.85, 0.00001) + f.collateralWeight.load("DAI", 0.9, 0.85, 0.00005) + f.collateralWeight.load("DOGE", 0.95, 0.9, 0.00002) + f.collateralWeight.load("ETH", 0.95, 0.9, 0.0004) + f.collateralWeight.load("STETH", 0.9, 0.85, 0.0012) + f.collateralWeight.load("ETHE", 0.9, 0.85, 0.0025) + f.collateralWeight.load("EUR", 0.99, 0.98, 0.00001) + f.collateralWeight.load("FB", 0.9, 0.85, 0.01) + f.collateralWeight.load("FIDA", 0.85, 0.8, 0.001) + f.collateralWeight.load("FTM", 0.85, 0.8, 0.0005) + f.collateralWeight.load("FTT", 0.95, 0.95, 0.0005) + f.collateralWeight.load("GBP", 0.99, 0.98, 0.00001) + f.collateralWeight.load("GBTC", 0.9, 0.85, 0.0025) + f.collateralWeight.load("GDX", 0.9, 0.85, 0.0025) + f.collateralWeight.load("GDXJ", 0.9, 0.85, 0.005) + f.collateralWeight.load("GLD", 0.9, 0.85, 0.005) + f.collateralWeight.load("GLXY", 0.9, 0.85, 0.005) + f.collateralWeight.load("GME", 0.9, 0.85, 0.005) + f.collateralWeight.load("GOOGL", 0.9, 0.85, 0.025) + f.collateralWeight.load("GRT", 0.9, 0.85, 0.00025) + f.collateralWeight.load("HKD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("HOLY", 0.9, 0.85, 0.0005) + f.collateralWeight.load("HOOD", 0.85, 0.8, 0.005) + f.collateralWeight.load("HT", 0.9, 0.85, 0.0003) + f.collateralWeight.load("HUSD", 1, 1, 0) + f.collateralWeight.load("HXRO", 0.85, 0.8, 0.001) + f.collateralWeight.load("IBVOL", 0.85, 0.8, 0.015) + f.collateralWeight.load("KIN", 0.85, 0.8, 0.000008) + f.collateralWeight.load("KNC", 0.95, 0.9, 0.001) + f.collateralWeight.load("LEO", 0.85, 0.8, 0.001) + f.collateralWeight.load("LINK", 0.95, 0.9, 0.0004) + f.collateralWeight.load("LRC", 0.85, 0.8, 0.0005) + f.collateralWeight.load("LTC", 0.95, 0.9, 0.0004) + f.collateralWeight.load("MATIC", 0.85, 0.8, 0.00004) + f.collateralWeight.load("MKR", 0.9, 0.85, 0.007) + f.collateralWeight.load("MOB", 0.6, 0.55, 0.005) + f.collateralWeight.load("MRNA", 0.9, 0.85, 0.005) + f.collateralWeight.load("MSTR", 0.9, 0.85, 0.008) + f.collateralWeight.load("NFLX", 0.9, 0.85, 0.01) + f.collateralWeight.load("NIO", 0.9, 0.85, 0.004) + f.collateralWeight.load("NOK", 0.9, 0.85, 0.001) + f.collateralWeight.load("NVDA", 0.9, 0.85, 0.01) + f.collateralWeight.load("OKB", 0.9, 0.85, 0.0003) + f.collateralWeight.load("OMG", 0.85, 0.8, 0.001) + f.collateralWeight.load("USDP", 1, 1, 0) + f.collateralWeight.load("PAXG", 0.95, 0.9, 0.002) + f.collateralWeight.load("PENN", 0.9, 0.85, 0.005) + f.collateralWeight.load("PFE", 0.9, 0.85, 0.004) + f.collateralWeight.load("PYPL", 0.9, 0.85, 0.008) + f.collateralWeight.load("RAY", 0.85, 0.8, 0.0005) + f.collateralWeight.load("REN", 0.9, 0.85, 0.00025) + f.collateralWeight.load("RSR", 0.85, 0.8, 0.0001) + f.collateralWeight.load("RUNE", 0.85, 0.8, 0.001) + f.collateralWeight.load("SECO", 0.9, 0.85, 0.0005) + f.collateralWeight.load("SGD", 0.99, 0.98, 0.00001) + f.collateralWeight.load("SLV", 0.9, 0.85, 0.0025) + f.collateralWeight.load("SNX", 0.85, 0.8, 0.001) + f.collateralWeight.load("SOL", 0.9, 0.85, 0.0004) + f.collateralWeight.load("STSOL", 0.9, 0.85, 0.0004) + f.collateralWeight.load("MSOL", 0.9, 0.85, 0.0004) + f.collateralWeight.load("SPY", 0.9, 0.85, 0.01) + f.collateralWeight.load("SQ", 0.9, 0.85, 0.008) + f.collateralWeight.load("SRM", 0.9, 0.85, 0.0005) + f.collateralWeight.load("SUSHI", 0.95, 0.9, 0.001) + f.collateralWeight.load("SXP", 0.9, 0.85, 0.0005) + f.collateralWeight.load("TLRY", 0.9, 0.85, 0.001) + f.collateralWeight.load("TOMO", 0.85, 0.8, 0.0005) + f.collateralWeight.load("TRX", 0.9, 0.85, 0.00001) + f.collateralWeight.load("TRY", 0.99, 0.98, 0.00001) + f.collateralWeight.load("TRYB", 0.9, 0.85, 0.00001) + f.collateralWeight.load("TSLA", 0.9, 0.85, 0.01) + f.collateralWeight.load("TSM", 0.9, 0.85, 0.015) + f.collateralWeight.load("TUSD", 1, 1, 0) + f.collateralWeight.load("TWTR", 0.9, 0.85, 0.004) + f.collateralWeight.load("UBER", 0.9, 0.85, 0.004) + f.collateralWeight.load("UNI", 0.95, 0.9, 0.001) + f.collateralWeight.load("USD", 1, 1, 0) + f.collateralWeight.load("USDC", 1, 1, 0) + f.collateralWeight.load("USDT", 0.975, 0.95, 0.00001) + f.collateralWeight.load("USO", 0.9, 0.85, 0.0025) + f.collateralWeight.load("WBTC", 0.975, 0.95, 0.005) + f.collateralWeight.load("WUSDC", 1, 1, 0) + f.collateralWeight.load("WUSDT", 0.975, 0.95, 0.00001) + f.collateralWeight.load("XAUT", 0.95, 0.9, 0.002) + f.collateralWeight.load("XRP", 0.95, 0.9, 0.00002) + f.collateralWeight.load("YFI", 0.9, 0.85, 0.015) + f.collateralWeight.load("ZAR", 0.99, 0.98, 0.00001) + f.collateralWeight.load("ZM", 0.9, 0.85, 0.01) + f.collateralWeight.load("ZRX", 0.85, 0.8, 0.001) if !f.GetAuthenticatedAPISupport(exchange.RestAuthentication) { return nil @@ -1639,7 +1639,7 @@ func (f *FTX) LoadCollateralWeightings() error { if !coins[i].Collateral { continue } - f.collateralWeight.loadTotal(currency.NewCode(coins[i].ID), coins[i].CollateralWeight) + f.collateralWeight.loadTotal(coins[i].ID, coins[i].CollateralWeight) } futures, err := f.GetFutures(ctx) @@ -1647,7 +1647,7 @@ func (f *FTX) LoadCollateralWeightings() error { return err } for i := range futures { - f.collateralWeight.loadIMF(currency.NewCode(futures[i].Underlying), futures[i].IMFFactor) + f.collateralWeight.loadIMF(futures[i].Underlying, futures[i].IMFFactor) } return nil @@ -1657,7 +1657,7 @@ func (c CollateralWeightHolder) isLoaded() bool { return len(c) > 0 } -func (c CollateralWeightHolder) loadTotal(code currency.Code, weighting float64) { +func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { butts, ok := c[code] if !ok { butts = CollateralWeight{Total: weighting} @@ -1667,7 +1667,7 @@ func (c CollateralWeightHolder) loadTotal(code currency.Code, weighting float64) c[code] = butts } -func (c CollateralWeightHolder) loadIMF(code currency.Code, imf float64) { +func (c CollateralWeightHolder) loadIMF(code string, imf float64) { butts, ok := c[code] if !ok { butts = CollateralWeight{IMFFactor: imf} @@ -1677,7 +1677,7 @@ func (c CollateralWeightHolder) loadIMF(code currency.Code, imf float64) { c[code] = butts } -func (c CollateralWeightHolder) load(code currency.Code, initial, total, imfFactor float64) { +func (c CollateralWeightHolder) load(code string, initial, total, imfFactor float64) { c[code] = CollateralWeight{ Initial: initial, Total: total, diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index 1131b71ab67..1095a274ca0 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -3,7 +3,6 @@ package ftx import ( "time" - "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -883,7 +882,7 @@ type StakeReward struct { } // CollateralWeightHolder stores collateral weights over the lifecycle of the application -type CollateralWeightHolder map[currency.Code]CollateralWeight +type CollateralWeightHolder map[string]CollateralWeight type CollateralWeight struct { Initial float64 diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 5070e9c663f..5974e9e2d56 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1272,15 +1272,23 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur return availableChains, nil } +func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice float64) float64 { + return positionSize * (markPrice - prevMarkPrice) +} + func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, error) { - var result *exchange.PNLResult + var result exchange.PNLResult if pnl.CalculateOffline { collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.Amount, pnl.EntryPrice, true) if err != nil { return nil, err } result.Collateral = decimal.NewFromFloat(collat) - return result, nil + // TODO add mark price somehow + // need mark price candles + mark price from 30 second ago candles + uPNL := f.CalculateUnrealisedPNL(pnl.Amount, pnl.MarkPrice, pnl.PrevMarkPrice) + result.UnrealisedPNL = decimal.NewFromFloat(uPNL) + return &result, nil } else { ctx := context.Background() info, err := f.GetAccountInfo(ctx) @@ -1289,7 +1297,7 @@ func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, er } if info.Liquidating || info.Collateral == 0 { result.IsLiquidated = true - return result, nil + return &result, nil } for i := range info.Positions { ftxSide := order.Side(info.Positions[i].Side) @@ -1311,7 +1319,7 @@ func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, er return nil, err } result.Collateral = decimal.NewFromFloat(collat) - return result, nil + return &result, nil } } } diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 25938ae94af..59e3cc714e7 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -147,6 +147,7 @@ func (f *Futures) UpsertPNLEntry(entry PNLHistory) { type PNLHistory struct { Time time.Time UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal } // Detail contains all properties of an order From b220fb74fec9b75a5f12de68b20957cf10bd1340 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 1 Dec 2021 17:46:17 +1100 Subject: [PATCH 032/171] more funcs to flesh out handling --- backtester/config/config_types.go | 4 +- .../portfolio/compliance/compliance_types.go | 12 +- .../eventhandlers/portfolio/portfolio.go | 36 +++--- .../eventhandlers/portfolio/portfolio_test.go | 10 +- .../ftxquarterlyfutures.go} | 33 +++--- .../ftxquarterlyfutures_test.go} | 2 +- backtester/eventtypes/signal/signal.go | 15 --- backtester/eventtypes/signal/signal_types.go | 6 - backtester/funding/funding_types.go | 1 + exchanges/ftx/ftx_websocket_test.go | 2 +- exchanges/order/futures.go | 108 ++++++++++++++++++ exchanges/order/order_types.go | 35 ------ 12 files changed, 158 insertions(+), 106 deletions(-) rename backtester/eventhandlers/strategies/{futures/futures.go => ftxquarterlyfutures/ftxquarterlyfutures.go} (88%) rename backtester/eventhandlers/strategies/{futures/futures_test.go => ftxquarterlyfutures/ftxquarterlyfutures_test.go} (99%) create mode 100644 exchanges/order/futures.go diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 16bfe3d6f0b..06e4681d39d 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -97,8 +97,8 @@ type PortfolioSettings struct { // Leverage rules are used to allow or limit the use of leverage in orders // when supported type Leverage struct { - CanUseLeverage bool `json:"can-use-leverage"` - MaximumOrdersWithLeverageRatio decimal.Decimal `json:"maximum-orders-with-leverage-ratio"` + CanUseLeverage bool `json:"can-use-leverage"` + MaximumOrdersWithLeverageRatio int64 `json:"maximum-orders-with-leverage-ratio"` // this means you can place an order with higher leverage rate. eg have $100 in collateral, // but place an order for $200 using 2x leverage MaximumOrderLeverageRate float64 `json:"maximum-leverage-rate"` diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index ae5a7bca37f..eaf5375ff11 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -29,10 +29,10 @@ type Snapshot struct { // SnapshotOrder adds some additional data that's only relevant for backtesting // to the order.Detail without adding to order.Detail type SnapshotOrder struct { - ClosePrice decimal.Decimal `json:"close-price"` - VolumeAdjustedPrice decimal.Decimal `json:"volume-adjusted-price"` - SlippageRate decimal.Decimal `json:"slippage-rate"` - CostBasis decimal.Decimal `json:"cost-basis"` - SpotOrder *order.Detail `json:"order-detail"` - FuturesOrder *order.Futures `json:"futures-order"` + ClosePrice decimal.Decimal `json:"close-price"` + VolumeAdjustedPrice decimal.Decimal `json:"volume-adjusted-price"` + SlippageRate decimal.Decimal `json:"slippage-rate"` + CostBasis decimal.Decimal `json:"cost-basis"` + SpotOrder *order.Detail `json:"order-detail"` + FuturesOrder *order.FuturesTracker `json:"futures-order"` } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 0d0455c1d3d..223d350d3d6 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -225,8 +225,8 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. // this means we're closing an order snap := lookup.ComplianceManager.GetLatestSnapshot() for i := range snap.Orders { - if ev.GetLinkedOrderID() == snap.Orders[i].FuturesOrder.OpeningPosition.ID { - snap.Orders[i].FuturesOrder.ClosingPosition = ev.GetOrder() + if ev.GetLinkedOrderID() == snap.Orders[i].FuturesOrder.ShortPositions.ID { + snap.Orders[i].FuturesOrder.LongPositions = ev.GetOrder() snap.Orders[i].FuturesOrder.RealisedPNL = snap.Orders[i].FuturesOrder.UnrealisedPNL } @@ -341,16 +341,16 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { var linked bool for i := range prevSnap.Orders { if prevSnap.Orders[i].FuturesOrder != nil && - prevSnap.Orders[i].FuturesOrder.OpeningPosition != nil && - prevSnap.Orders[i].FuturesOrder.OpeningPosition.ID == fillEvent.GetLinkedOrderID() { - prevSnap.Orders[i].FuturesOrder.ClosingPosition = fo + prevSnap.Orders[i].FuturesOrder.ShortPositions != nil && + prevSnap.Orders[i].FuturesOrder.ShortPositions.ID == fillEvent.GetLinkedOrderID() { + prevSnap.Orders[i].FuturesOrder.LongPositions = fo linked = true } } if !linked { - snapOrder.FuturesOrder = &gctorder.Futures{ - Side: fillEvent.GetDirection(), - OpeningPosition: fo, + snapOrder.FuturesOrder = &gctorder.FuturesTracker{ + CurrentDirection: fillEvent.GetDirection(), + ShortPositions: fo, } prevSnap.Orders = append(prevSnap.Orders, snapOrder) } @@ -559,24 +559,24 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla if snapshot.Orders[i].FuturesOrder == nil { continue } - if snapshot.Orders[i].FuturesOrder.ClosingPosition != nil { + if snapshot.Orders[i].FuturesOrder.LongPositions != nil { continue } - if snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage == 0 { - snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage = 1 + if snapshot.Orders[i].FuturesOrder.ShortPositions.Leverage == 0 { + snapshot.Orders[i].FuturesOrder.ShortPositions.Leverage = 1 } var result *gctexchange.PNLResult result, err = settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ Asset: e.GetAssetType(), - Leverage: snapshot.Orders[i].FuturesOrder.OpeningPosition.Leverage, - EntryPrice: snapshot.Orders[i].FuturesOrder.OpeningPosition.Price, - OpeningAmount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, + Leverage: snapshot.Orders[i].FuturesOrder.ShortPositions.Leverage, + EntryPrice: snapshot.Orders[i].FuturesOrder.ShortPositions.Price, + OpeningAmount: snapshot.Orders[i].FuturesOrder.ShortPositions.Amount, CurrentPrice: e.GetClosePrice().InexactFloat64(), CollateralAmount: funds.AvailableFunds(), CalculateOffline: true, CollateralCurrency: funds.CollateralCurrency(), - Amount: snapshot.Orders[i].FuturesOrder.OpeningPosition.Amount, + Amount: snapshot.Orders[i].FuturesOrder.ShortPositions.Amount, MarkPrice: e.GetClosePrice().InexactFloat64(), PrevMarkPrice: e.GetOpenPrice().InexactFloat64(), }) @@ -588,14 +588,14 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla funds.Liquidate() snapshot.Orders[i].FuturesOrder.UnrealisedPNL = decimal.Zero snapshot.Orders[i].FuturesOrder.RealisedPNL = decimal.Zero - or := snapshot.Orders[i].FuturesOrder.OpeningPosition.Copy() + or := snapshot.Orders[i].FuturesOrder.ShortPositions.Copy() or.Side = common.Liquidated - snapshot.Orders[i].FuturesOrder.ClosingPosition = &or + snapshot.Orders[i].FuturesOrder.LongPositions = &or return nil } snapshot.Orders[i].FuturesOrder.RealisedPNL.Add(snapshot.Orders[i].FuturesOrder.UnrealisedPNL) snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.UnrealisedPNL - snapshot.Orders[i].FuturesOrder.OpeningPosition.UnrealisedPNL = result.UnrealisedPNL + snapshot.Orders[i].FuturesOrder.ShortPositions.UnrealisedPNL = result.UnrealisedPNL snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ Time: e.GetTime(), UnrealisedPNL: result.UnrealisedPNL, diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index c78c38cfd65..678f81cc49f 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -716,9 +716,9 @@ func TestCalculatePNL(t *testing.T) { t.Errorf("received: %v, expected: %v", err, nil) } - futuresOrder := &gctorder.Futures{ - Side: gctorder.Short, - OpeningPosition: &gctorder.Detail{ + futuresOrder := &gctorder.FuturesTracker{ + CurrentDirection: gctorder.Short, + ShortPositions: &gctorder.Detail{ Price: 1336, Amount: 20, Exchange: exch, @@ -756,7 +756,7 @@ func TestCalculatePNL(t *testing.T) { err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { ClosePrice: decimal.NewFromInt(1336), - SpotOrder: futuresOrder.OpeningPosition, + SpotOrder: futuresOrder.ShortPositions, }, }, tt, 1, false) err = p.CalculatePNL(ev, nil) @@ -765,7 +765,7 @@ func TestCalculatePNL(t *testing.T) { } // coverage of logic - futuresOrder.ClosingPosition = futuresOrder.OpeningPosition + futuresOrder.LongPositions = futuresOrder.ShortPositions err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { diff --git a/backtester/eventhandlers/strategies/futures/futures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go similarity index 88% rename from backtester/eventhandlers/strategies/futures/futures.go rename to backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go index 02cb6ac8676..937255cc089 100644 --- a/backtester/eventhandlers/strategies/futures/futures.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go @@ -1,4 +1,4 @@ -package futures +package ftxquarterlyfutures import ( "errors" @@ -72,7 +72,7 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol if err != nil { return nil, err } - es.SetPrice(d.Latest().ClosePrice()) + es.SetPrice(d.Latest().GetClosePrice()) if offset := d.Offset(); offset <= int(s.rsiPeriod.IntPart()) { es.AppendReason("Not enough data for signal generation") @@ -99,17 +99,17 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol return nil, err } - var unrealisedOrder *order.Detail + var o *order.FuturesTracker for i := range currentOrders.Orders { if currentOrders.Orders[i].FuturesOrder != nil { - if currentOrders.Orders[i].FuturesOrder.ClosingPosition == nil { - if currentOrders.Orders[i].FuturesOrder.Side == order.Short || currentOrders.Orders[i].FuturesOrder.Side == order.Long { - unrealisedOrder = currentOrders.Orders[i].FuturesOrder.OpeningPosition + if currentOrders.Orders[i].FuturesOrder.LongPositions == nil { + if currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Short || currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Long { + o = currentOrders.Orders[i].FuturesOrder } } } } - if unrealisedOrder == nil { + if o.ShortPositions == nil { switch { case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): es.SetDirection(order.Short) @@ -120,22 +120,21 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol } es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) } else { - p := decimal.NewFromFloat(unrealisedOrder.Price) + price := decimal.NewFromFloat(o.ShortPositions.Price) if latestRSIValue.LessThanOrEqual(s.rsiLow) || latestRSIValue.GreaterThanOrEqual(s.rsiHigh) || - (!s.stopLoss.IsZero() && latest.ClosePrice().LessThanOrEqual(s.stopLoss)) || - (!s.takeProfit.IsZero() && latest.ClosePrice().GreaterThanOrEqual(s.takeProfit)) || - (!s.trailingStop.IsZero() && latest.ClosePrice().Sub(p).Div(p).Mul(decimal.NewFromInt(100)).LessThanOrEqual(s.trailingStop)) || - unrealisedOrder.UnrealisedPNL.GreaterThanOrEqual(s.highestUnrealised) || - unrealisedOrder.UnrealisedPNL.LessThanOrEqual(s.lowestUnrealised) { + (!s.stopLoss.IsZero() && latest.GetClosePrice().LessThanOrEqual(s.stopLoss)) || + (!s.takeProfit.IsZero() && latest.GetClosePrice().GreaterThanOrEqual(s.takeProfit)) || + (!s.trailingStop.IsZero() && latest.GetClosePrice().Sub(price).Div(price).Mul(decimal.NewFromInt(100)).LessThanOrEqual(s.trailingStop)) || + o.ShortPositions.UnrealisedPNL.GreaterThanOrEqual(s.highestUnrealised) || + o.ShortPositions.UnrealisedPNL.LessThanOrEqual(s.lowestUnrealised) { // set up the counter order to close the position - es.SetAmount(decimal.NewFromFloat(unrealisedOrder.Amount)) - if unrealisedOrder.Side == order.Short { + es.SetAmount(decimal.NewFromFloat(o.ShortPositions.Amount)) + if o.ShortPositions.Side == order.Short { es.SetDirection(order.Long) - } else if unrealisedOrder.Side == order.Long { + } else if o.ShortPositions.Side == order.Long { es.SetDirection(order.Short) } - es.SetCloseOrderID(unrealisedOrder.ID) } } diff --git a/backtester/eventhandlers/strategies/futures/futures_test.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go similarity index 99% rename from backtester/eventhandlers/strategies/futures/futures_test.go rename to backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go index d4585a61ff0..aa3e64063e8 100644 --- a/backtester/eventhandlers/strategies/futures/futures_test.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go @@ -1,4 +1,4 @@ -package futures +package ftxquarterlyfutures import ( "errors" diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index d4a841401e4..a52f0aff724 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -3,7 +3,6 @@ package signal import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -66,17 +65,3 @@ func (s *Signal) GetAmount() decimal.Decimal { func (s *Signal) SetAmount(d decimal.Decimal) { s.Amount = d } - -// SetCloseOrderID links an existing order id -// for a futures order set to be closed -func (s *Signal) SetCloseOrderID(id string) { - if s.AssetType == asset.Futures { - s.CloseOrderID = id - } -} - -// GetLinkedOrderID returns the order ID of a -// linked futures order -func (s *Signal) GetLinkedOrderID() string { - return s.CloseOrderID -} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index 06e017d54e3..81fe7e5806a 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -18,7 +18,6 @@ type Event interface { GetSellLimit() decimal.Decimal GetBuyLimit() decimal.Decimal GetAmount() decimal.Decimal - GetLinkedOrderID() string } // Signal contains everything needed for a strategy to raise a signal event @@ -43,9 +42,4 @@ type Signal struct { // the order will not be placed Amount decimal.Decimal Direction order.Side - // CloseOrderID is the order ID of the futures order - // to that is being closed. This linking allows the - // more detailed order.Futures struct to close out - // and have more detailed performance tracking - CloseOrderID string } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 84656557e7b..1d3a78bdb73 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -114,6 +114,7 @@ type Item struct { currency currency.Code initialFunds decimal.Decimal available decimal.Decimal + weightedAvailable decimal.Decimal reserved decimal.Decimal transferFee decimal.Decimal pairedWith *Item diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index 4326592c42c..bae0457cb79 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -358,7 +358,7 @@ func TestParsingMarketsData(t *testing.T) { "future": { "name": "ADA-0626", "underlying": "ADA", - "description": "Cardano June 2020 Futures", + "description": "Cardano June 2020 FuturesTracker", "type": "future", "expiry": "2020-06-26T003:00:00+00:00", "perpetual": false, "expired": false, diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go new file mode 100644 index 00000000000..b13b756f5c2 --- /dev/null +++ b/exchanges/order/futures.go @@ -0,0 +1,108 @@ +package order + +import ( + "errors" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +// IsShort returns if the side is short +func (s Side) IsShort() bool { + return s == Short || s == Sell +} + +// IsLong returns if the side is long +func (s Side) IsLong() bool { + return s == Long || s == Buy +} + +// TrackNewOrder knows how things are going for a given +// futures contract +func (f *FuturesTracker) TrackNewOrder(d *Detail) error { + if !f.ContractPair.Equal(d.Pair) { + return errors.New("not the same") + } + if f.Exchange != d.Exchange { + return errors.New("not the same") + } + if f.Asset != d.AssetType { + return errors.New("not the same") + } + + if d.Side.IsShort() { + f.ShortPositions = append(f.ShortPositions, d.Copy()) + } else { + f.LongPositions = append(f.LongPositions, d.Copy()) + } + var shortSide, longSide decimal.Decimal + for i := range f.ShortPositions { + shortSide = shortSide.Add(decimal.NewFromFloat(f.ShortPositions[i].Amount)) + } + for i := range f.LongPositions { + longSide = shortSide.Add(decimal.NewFromFloat(f.ShortPositions[i].Amount)) + } + if f.CurrentDirection.IsLong() { + f.Exposure = longSide.Sub(shortSide) + } else { + f.Exposure = shortSide.Sub(longSide) + } + if f.Exposure.Equal(decimal.Zero) { + // the order is closed + f.Status = Closed + } else { + f.Status = Open + } + if f.Exposure.IsNegative() { + // tracking here has changed! + if f.CurrentDirection.IsLong() { + f.CurrentDirection = Short + } else { + f.CurrentDirection = Long + } + } + + return nil +} + +// UpsertPNLEntry upserts an entry to PNLHistory field +// with some basic checks +func (f *FuturesTracker) UpsertPNLEntry(entry PNLHistory) { + if entry.Time.IsZero() { + return + } + for i := range f.PNLHistory { + if entry.Time.Equal(f.PNLHistory[i].Time) { + f.PNLHistory[i] = entry + return + } + } + f.PNLHistory = append(f.PNLHistory, entry) +} + +// FuturesTracker order is a concept which holds both the opening and closing orders +// for a futures contract. This allows for PNL calculations +type FuturesTracker struct { + Exchange string + Asset asset.Item + ContractPair currency.Pair + UnderlyingAsset currency.Code + Exposure decimal.Decimal + CurrentDirection Side + Status Status + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal + ShortPositions []Detail + LongPositions []Detail + PNLHistory []PNLHistory +} + +// PNLHistory tracks how a futures contract +// pnl is going over the history of exposure +type PNLHistory struct { + Time time.Time + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal +} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 59e3cc714e7..fce4a19ab40 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -115,41 +115,6 @@ type ModifyResponse struct { OrderID string } -// Futures order is a concept which holds both the opening and closing orders -// for a futures contract. This allows for PNL calculations -type Futures struct { - Side Side - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - UnderlyingAsset currency.Code - CollateralCurrency currency.Code - OpeningPosition *Detail - ClosingPosition *Detail - ClosingPositions []Detail - PNLHistory []PNLHistory -} - -// UpsertPNLEntry upserts an entry to PNLHistory field -// with some basic checks -func (f *Futures) UpsertPNLEntry(entry PNLHistory) { - if entry.Time.IsZero() { - return - } - for i := range f.PNLHistory { - if entry.Time.Equal(f.PNLHistory[i].Time) { - f.PNLHistory[i] = entry - return - } - } - f.PNLHistory = append(f.PNLHistory, entry) -} - -type PNLHistory struct { - Time time.Time - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal -} - // Detail contains all properties of an order // Each exchange has their own requirements, so not all fields // are required to be populated From bda70db59009152f5b9956938b8b16d75c0982b1 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 2 Dec 2021 17:08:32 +1100 Subject: [PATCH 033/171] Adds more links, just can't fit the pieces together :( --- .../portfolio/compliance/compliance_types.go | 18 ++--- .../eventhandlers/portfolio/portfolio.go | 80 ++++++++----------- .../eventhandlers/portfolio/portfolio_test.go | 10 +-- .../eventhandlers/portfolio/risk/risk.go | 2 +- .../eventhandlers/portfolio/risk/risk_test.go | 2 +- .../statistics/currencystatistics.go | 4 +- .../statistics/currencystatistics_test.go | 16 ++-- backtester/eventtypes/fill/fill_types.go | 2 - backtester/funding/collateral.go | 5 ++ backtester/funding/funding_types.go | 1 + backtester/report/report.go | 10 +-- backtester/report/report_test.go | 6 +- exchanges/exchange.go | 21 +++++ exchanges/ftx/ftx_wrapper.go | 11 ++- exchanges/interfaces.go | 2 + exchanges/order/futures.go | 7 ++ 16 files changed, 114 insertions(+), 83 deletions(-) diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index eaf5375ff11..8ec69cb28e4 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -21,18 +21,18 @@ type Manager struct { // Snapshot consists of the timestamp the snapshot is from, along with all orders made // up until that time type Snapshot struct { - Orders []SnapshotOrder `json:"orders"` - Timestamp time.Time `json:"timestamp"` - Offset int64 `json:"offset"` + Offset int64 `json:"offset"` + Timestamp time.Time `json:"timestamp"` + Orders []SnapshotOrder `json:"orders"` + FuturesTracker *order.FuturesTracker `json:"futures-order"` } // SnapshotOrder adds some additional data that's only relevant for backtesting // to the order.Detail without adding to order.Detail type SnapshotOrder struct { - ClosePrice decimal.Decimal `json:"close-price"` - VolumeAdjustedPrice decimal.Decimal `json:"volume-adjusted-price"` - SlippageRate decimal.Decimal `json:"slippage-rate"` - CostBasis decimal.Decimal `json:"cost-basis"` - SpotOrder *order.Detail `json:"order-detail"` - FuturesOrder *order.FuturesTracker `json:"futures-order"` + ClosePrice decimal.Decimal `json:"close-price"` + VolumeAdjustedPrice decimal.Decimal `json:"volume-adjusted-price"` + SlippageRate decimal.Decimal `json:"slippage-rate"` + CostBasis decimal.Decimal `json:"cost-basis"` + Order *order.Detail `json:"order-detail"` } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 223d350d3d6..df1e5eed1e5 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -75,8 +75,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi Interval: ev.GetInterval(), Reason: ev.GetReason(), }, - Direction: ev.GetDirection(), - LinkedOrderID: ev.GetLinkedOrderID(), + Direction: ev.GetDirection(), } if ev.GetDirection() == "" { return o, errInvalidDirection @@ -130,12 +129,23 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi } else { sizingFunds = pReader.QuoteAvailable() } - } else if ev.GetAssetType() == asset.Futures { + } else if ev.GetAssetType().IsFutures() { cReader, err := funds.GetCollateralReader() if err != nil { return nil, err } sizingFunds = cReader.AvailableFunds() + sizingFunds, err = lookup.Exchange.ScaleCollateral(&gctexchange.CollateralCalculator{ + CollateralCurrency: cReader.CollateralCurrency(), + Asset: ev.GetAssetType(), + Side: ev.GetDirection(), + CollateralAmount: sizingFunds, + EntryPrice: ev.GetPrice().InexactFloat64(), + }) + if err != nil { + return nil, err + } + } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) @@ -221,18 +231,6 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. } var err error - if ev.GetLinkedOrderID() != "" { - // this means we're closing an order - snap := lookup.ComplianceManager.GetLatestSnapshot() - for i := range snap.Orders { - if ev.GetLinkedOrderID() == snap.Orders[i].FuturesOrder.ShortPositions.ID { - snap.Orders[i].FuturesOrder.LongPositions = ev.GetOrder() - snap.Orders[i].FuturesOrder.RealisedPNL = snap.Orders[i].FuturesOrder.UnrealisedPNL - - } - } - } - if ev.GetAssetType() == asset.Spot { fp, err := funding.GetPairReleaser() if err != nil { @@ -334,25 +332,12 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { SlippageRate: fillEvent.GetSlippageRate(), CostBasis: price.Mul(amount).Add(fee), } - if fo.AssetType == asset.Spot { - snapOrder.SpotOrder = fo - prevSnap.Orders = append(prevSnap.Orders, snapOrder) - } else if fo.AssetType == asset.Futures { - var linked bool - for i := range prevSnap.Orders { - if prevSnap.Orders[i].FuturesOrder != nil && - prevSnap.Orders[i].FuturesOrder.ShortPositions != nil && - prevSnap.Orders[i].FuturesOrder.ShortPositions.ID == fillEvent.GetLinkedOrderID() { - prevSnap.Orders[i].FuturesOrder.LongPositions = fo - linked = true - } - } - if !linked { - snapOrder.FuturesOrder = &gctorder.FuturesTracker{ - CurrentDirection: fillEvent.GetDirection(), - ShortPositions: fo, - } - prevSnap.Orders = append(prevSnap.Orders, snapOrder) + snapOrder.Order = fo + prevSnap.Orders = append(prevSnap.Orders, snapOrder) + if fo.AssetType == asset.Futures { + err = prevSnap.FuturesTracker.TrackNewOrder(fillEvent.GetOrder()) + if err != nil { + return err } } } @@ -546,6 +531,9 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // CalculatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.ICollateralReleaser) error { + if !e.GetAssetType().IsFutures() { + return nil // or err + } settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] if !ok { return errNoPortfolioSettings @@ -556,16 +544,19 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla return err } for i := range snapshot.Orders { - if snapshot.Orders[i].FuturesOrder == nil { - continue - } - if snapshot.Orders[i].FuturesOrder.LongPositions != nil { - continue - } - if snapshot.Orders[i].FuturesOrder.ShortPositions.Leverage == 0 { - snapshot.Orders[i].FuturesOrder.ShortPositions.Leverage = 1 + if snapshot.FuturesTracker == nil { + snapshot.FuturesTracker = &gctorder.FuturesTracker{ + Exchange: e.GetExchange(), + Asset: e.GetAssetType(), + ContractPair: e.Pair(), + UnderlyingAsset: funds.UnderlyingAsset(), + } } + // should futures tracker reference the interface function? + // eg it can call calculate pnl via using the exchange's implementation? + snapshot.FuturesTracker.TrackNewOrder() + var result *gctexchange.PNLResult result, err = settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ Asset: e.GetAssetType(), @@ -588,14 +579,11 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla funds.Liquidate() snapshot.Orders[i].FuturesOrder.UnrealisedPNL = decimal.Zero snapshot.Orders[i].FuturesOrder.RealisedPNL = decimal.Zero - or := snapshot.Orders[i].FuturesOrder.ShortPositions.Copy() - or.Side = common.Liquidated - snapshot.Orders[i].FuturesOrder.LongPositions = &or + snapshot.Orders[i].FuturesOrder.CurrentDirection = common.Liquidated return nil } snapshot.Orders[i].FuturesOrder.RealisedPNL.Add(snapshot.Orders[i].FuturesOrder.UnrealisedPNL) snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.UnrealisedPNL - snapshot.Orders[i].FuturesOrder.ShortPositions.UnrealisedPNL = result.UnrealisedPNL snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ Time: e.GetTime(), UnrealisedPNL: result.UnrealisedPNL, diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 678f81cc49f..147c737e1a5 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -583,7 +583,7 @@ func TestGetSnapshotAtTime(t *testing.T) { } err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { - SpotOrder: &gctorder.Detail{ + Order: &gctorder.Detail{ Exchange: "exch", AssetType: asset.Spot, Pair: cp, @@ -611,7 +611,7 @@ func TestGetSnapshotAtTime(t *testing.T) { if len(ss.Orders) != 1 { t.Fatal("expected 1") } - if ss.Orders[0].SpotOrder.Amount != 1337 { + if ss.Orders[0].Order.Amount != 1337 { t.Error("expected 1") } } @@ -635,7 +635,7 @@ func TestGetLatestSnapshot(t *testing.T) { } err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { - SpotOrder: &gctorder.Detail{ + Order: &gctorder.Detail{ Exchange: "exch", AssetType: asset.Spot, Pair: cp, @@ -654,7 +654,7 @@ func TestGetLatestSnapshot(t *testing.T) { err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ ss[0].Orders[0], { - SpotOrder: &gctorder.Detail{ + Order: &gctorder.Detail{ Exchange: "exch", AssetType: asset.Spot, Pair: cp, @@ -756,7 +756,7 @@ func TestCalculatePNL(t *testing.T) { err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ { ClosePrice: decimal.NewFromInt(1336), - SpotOrder: futuresOrder.ShortPositions, + Order: futuresOrder.ShortPositions, }, }, tt, 1, false) err = p.CalculatePNL(ev, nil) diff --git a/backtester/eventhandlers/portfolio/risk/risk.go b/backtester/eventhandlers/portfolio/risk/risk.go index c141e328558..0e089d187be 100644 --- a/backtester/eventhandlers/portfolio/risk/risk.go +++ b/backtester/eventhandlers/portfolio/risk/risk.go @@ -60,7 +60,7 @@ func existingLeverageRatio(s compliance.Snapshot) decimal.Decimal { } var ordersWithLeverage decimal.Decimal for o := range s.Orders { - if s.Orders[o].SpotOrder.Leverage != 0 { + if s.Orders[o].Order.Leverage != 0 { ordersWithLeverage = ordersWithLeverage.Add(decimal.NewFromInt(1)) } } diff --git a/backtester/eventhandlers/portfolio/risk/risk_test.go b/backtester/eventhandlers/portfolio/risk/risk_test.go index 553749085be..3538eb9d8a9 100644 --- a/backtester/eventhandlers/portfolio/risk/risk_test.go +++ b/backtester/eventhandlers/portfolio/risk/risk_test.go @@ -117,7 +117,7 @@ func TestEvaluateOrder(t *testing.T) { _, err = r.EvaluateOrder(o, h, compliance.Snapshot{ Orders: []compliance.SnapshotOrder{ { - SpotOrder: &gctorder.Detail{ + Order: &gctorder.Detail{ Leverage: 3, }, }, diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 97ddc178074..b854ba63594 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -28,9 +28,9 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e last := c.Events[len(c.Events)-1] lastPrice := last.DataEvent.GetClosePrice() for i := range last.Transactions.Orders { - if last.Transactions.Orders[i].SpotOrder.Side == gctorder.Buy { + if last.Transactions.Orders[i].Order.Side == gctorder.Buy { c.BuyOrders++ - } else if last.Transactions.Orders[i].SpotOrder.Side == gctorder.Sell { + } else if last.Transactions.Orders[i].Order.Side == gctorder.Sell { c.SellOrders++ } } diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index aa54961fca9..57efdcd47d6 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -45,14 +45,14 @@ func TestCalculateResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - SpotOrder: &order.Detail{Side: order.Buy}, + Order: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - SpotOrder: &order.Detail{Side: order.Sell}, + Order: &order.Detail{Side: order.Sell}, }, }, }, @@ -84,14 +84,14 @@ func TestCalculateResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - SpotOrder: &order.Detail{Side: order.Buy}, + Order: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - SpotOrder: &order.Detail{Side: order.Sell}, + Order: &order.Detail{Side: order.Sell}, }, }, }, @@ -182,14 +182,14 @@ func TestPrintResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - SpotOrder: &order.Detail{Side: order.Buy}, + Order: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - SpotOrder: &order.Detail{Side: order.Sell}, + Order: &order.Detail{Side: order.Sell}, }, }, }, @@ -221,14 +221,14 @@ func TestPrintResults(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1338), SlippageRate: decimal.NewFromInt(1338), CostBasis: decimal.NewFromInt(1338), - SpotOrder: &order.Detail{Side: order.Buy}, + Order: &order.Detail{Side: order.Buy}, }, { ClosePrice: decimal.NewFromInt(1337), VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1337), CostBasis: decimal.NewFromInt(1337), - SpotOrder: &order.Detail{Side: order.Sell}, + Order: &order.Detail{Side: order.Sell}, }, }, }, diff --git a/backtester/eventtypes/fill/fill_types.go b/backtester/eventtypes/fill/fill_types.go index 55129c4ac76..f172357a267 100644 --- a/backtester/eventtypes/fill/fill_types.go +++ b/backtester/eventtypes/fill/fill_types.go @@ -19,7 +19,6 @@ type Fill struct { ExchangeFee decimal.Decimal `json:"exchange-fee"` Slippage decimal.Decimal `json:"slippage"` Order *order.Detail `json:"-"` - LinkedOrderID string `json:"linked-order-id"` } // Event holds all functions required to handle a fill event @@ -37,5 +36,4 @@ type Event interface { GetExchangeFee() decimal.Decimal SetExchangeFee(decimal.Decimal) GetOrder() *order.Detail - GetLinkedOrderID() string } diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index fcfc93fb288..4fc04be067a 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -30,6 +30,11 @@ func (c *Collateral) ContractCurrency() currency.Code { return c.Contract.currency } +func (c *Collateral) UnderlyingAsset() currency.Code { + // somehow get the underlying + return c.Contract.currency +} + func (c *Collateral) CollateralCurrency() currency.Code { return c.Collateral.currency } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 1d3a78bdb73..11b0b494d87 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -86,6 +86,7 @@ type IPairReader interface { // ICollateralReader is used to read data from // collateral pairs type ICollateralReader interface { + UnderlyingAsset() currency.Code ContractCurrency() currency.Code CollateralCurrency() currency.Code InitialFunds() decimal.Decimal diff --git a/backtester/report/report.go b/backtester/report/report.go index 152aa7eb412..a346d1116b2 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -223,15 +223,15 @@ func (d *Data) enhanceCandles() error { } } for k := range statsForCandles.FinalOrders.Orders { - if statsForCandles.FinalOrders.Orders[k].SpotOrder == nil || - !statsForCandles.FinalOrders.Orders[k].SpotOrder.Date.Equal(d.OriginalCandles[intVal].Candles[j].Time) { + if statsForCandles.FinalOrders.Orders[k].Order == nil || + !statsForCandles.FinalOrders.Orders[k].Order.Date.Equal(d.OriginalCandles[intVal].Candles[j].Time) { continue } // an order was placed here, can enhance chart! enhancedCandle.MadeOrder = true - enhancedCandle.OrderAmount = decimal.NewFromFloat(statsForCandles.FinalOrders.Orders[k].SpotOrder.Amount) - enhancedCandle.PurchasePrice = statsForCandles.FinalOrders.Orders[k].SpotOrder.Price - enhancedCandle.OrderDirection = statsForCandles.FinalOrders.Orders[k].SpotOrder.Side + enhancedCandle.OrderAmount = decimal.NewFromFloat(statsForCandles.FinalOrders.Orders[k].Order.Amount) + enhancedCandle.PurchasePrice = statsForCandles.FinalOrders.Orders[k].Order.Price + enhancedCandle.OrderDirection = statsForCandles.FinalOrders.Orders[k].Order.Side if enhancedCandle.OrderDirection == order.Buy { enhancedCandle.Colour = "green" enhancedCandle.Position = "aboveBar" diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index 145b342fcab..496a85502bf 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -410,7 +410,7 @@ func TestEnhanceCandles(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1), CostBasis: decimal.NewFromInt(1337), - SpotOrder: nil, + Order: nil, }, }, Timestamp: tt, @@ -427,7 +427,7 @@ func TestEnhanceCandles(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1), CostBasis: decimal.NewFromInt(1337), - SpotOrder: &gctorder.Detail{ + Order: &gctorder.Detail{ Date: tt, Side: gctorder.Buy, }, @@ -447,7 +447,7 @@ func TestEnhanceCandles(t *testing.T) { VolumeAdjustedPrice: decimal.NewFromInt(1337), SlippageRate: decimal.NewFromInt(1), CostBasis: decimal.NewFromInt(1337), - SpotOrder: &gctorder.Detail{ + Order: &gctorder.Detail{ Date: tt, Side: gctorder.Sell, }, diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 62f5b229240..1a265b2c912 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1462,6 +1462,14 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ return nil, common.ErrFunctionNotSupported } +type CollateralCalculator struct { + CollateralCurrency currency.Code + Asset asset.Item + Side order.Side + CollateralAmount decimal.Decimal + EntryPrice float64 +} + type PNLCalculator struct { CalculateOffline bool Underlying currency.Code @@ -1517,3 +1525,16 @@ func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { return result, nil } + +// ScaleCollateral is an overridable function to allow PNL to be calculated on an +// open position +// It will also determine whether the position is considered to be liquidated +// for live trading, an overrided function may wish to confirm the liquidation by +// requesting the status of the asset +func (b *Base) ScaleCollateral(calc *CollateralCalculator) (decimal.Decimal, error) { + if !calc.Asset.IsFutures() { + return decimal.Zero, common.ErrFunctionNotSupported + } + + return calc.CollateralAmount, nil +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 5974e9e2d56..5005c7952d4 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1276,10 +1276,19 @@ func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice floa return positionSize * (markPrice - prevMarkPrice) } +func (f *FTX) ScaleCollateral(calculator exchange.CollateralCalculator) (decimal.Decimal, error) { + collat, err := f.CalculateCollateral(calculator.CollateralCurrency, calculator.CollateralAmount.InexactFloat64(), calculator.EntryPrice, true) + if err != nil { + return decimal.Zero, err + } + + return decimal.NewFromFloat(collat), nil +} + func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, error) { var result exchange.PNLResult if pnl.CalculateOffline { - collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.Amount, pnl.EntryPrice, true) + collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.CollateralAmount.InexactFloat64(), pnl.EntryPrice, true) if err != nil { return nil, err } diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index a5ea3ceed68..efb60f83b40 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -5,6 +5,7 @@ import ( "sync" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/account" @@ -81,6 +82,7 @@ type IBotExchange interface { DisableRateLimiter() error EnableRateLimiter() error CalculatePNL(*PNLCalculator) (*PNLResult, error) + ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) CurrencyStateManagement GetWebsocket() (*stream.Websocket, error) diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b13b756f5c2..df0180a81d6 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -32,6 +32,13 @@ func (f *FuturesTracker) TrackNewOrder(d *Detail) error { return errors.New("not the same") } + for i := range f.ShortPositions { + if f.ShortPositions[i].ID == d.ID { + f.ShortPositions[i] = d.Copy() + break + } + } + if d.Side.IsShort() { f.ShortPositions = append(f.ShortPositions, d.Copy()) } else { From 9696f2ccebedaf66c9fd178fbf4a06facd67e6cc Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 6 Dec 2021 17:27:17 +1100 Subject: [PATCH 034/171] Greatly expands futures order processing --- backtester/backtest/backtest.go | 12 +-- backtester/config/config_types.go | 4 +- engine/order_manager_types.go | 1 + exchanges/exchange.go | 26 +++--- exchanges/ftx/ftx_websocket_test.go | 1 + exchanges/ftx/ftx_wrapper.go | 12 +-- exchanges/interfaces.go | 13 ++- exchanges/order/futures.go | 124 ++++++++++++++++++++-------- exchanges/order/futures_test.go | 121 +++++++++++++++++++++++++++ exchanges/order/futures_types.go | 60 ++++++++++++++ 10 files changed, 310 insertions(+), 64 deletions(-) create mode 100644 exchanges/order/futures_test.go create mode 100644 exchanges/order/futures_types.go diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 1446e189743..3d0cbb6ce50 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -227,19 +227,19 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, return nil, fmt.Errorf("could not format currency %v, %w", curr, err) } curr = curr.Format(requestFormat.Delimiter, requestFormat.Uppercase) - var avail, enab currency.Pairs + var avail, enabled currency.Pairs avail, err = exch.GetAvailablePairs(a) if err != nil { return nil, fmt.Errorf("could not format currency %v, %w", curr, err) } - enab, err = exch.GetEnabledPairs(a) + enabled, err = exch.GetEnabledPairs(a) if err != nil { return nil, fmt.Errorf("could not format currency %v, %w", curr, err) } avail = avail.Add(curr) - enab = enab.Add(curr) - err = exch.SetPairs(enab, a, true) + enabled = enabled.Add(curr) + err = exch.SetPairs(enabled, a, true) if err != nil { return nil, fmt.Errorf("could not format currency %v, %w", curr, err) } @@ -252,7 +252,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, } if cfg.CurrencySettings[i].FuturesDetails != nil { - portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio + portSet.MaximumOrdersWithLeverageRatio = decimal.NewFromFloat(cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio) portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate } portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet @@ -551,7 +551,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange lev = exchange.Leverage{ CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, - MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, + MaximumOrdersWithLeverageRatio: decimal.NewFromFloat(cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio), } } resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 06e4681d39d..1dcbac296ea 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -97,8 +97,8 @@ type PortfolioSettings struct { // Leverage rules are used to allow or limit the use of leverage in orders // when supported type Leverage struct { - CanUseLeverage bool `json:"can-use-leverage"` - MaximumOrdersWithLeverageRatio int64 `json:"maximum-orders-with-leverage-ratio"` + CanUseLeverage bool `json:"can-use-leverage"` + MaximumOrdersWithLeverageRatio float64 `json:"maximum-orders-with-leverage-ratio"` // this means you can place an order with higher leverage rate. eg have $100 in collateral, // but place an order for $200 using 2x leverage MaximumOrderLeverageRate float64 `json:"maximum-leverage-rate"` diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index 4bbb73e937c..ce496b9d530 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -42,6 +42,7 @@ type store struct { commsManager iCommsManager exchangeManager iExchangeManager wg *sync.WaitGroup + futuresTrackers []order.ExchangeFuturesTracker } // OrderManager processes and stores orders across enabled exchanges diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 1a265b2c912..4dd80745a49 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1471,20 +1471,18 @@ type CollateralCalculator struct { } type PNLCalculator struct { - CalculateOffline bool - Underlying currency.Code - OrderID string - Asset asset.Item - Side order.Side - Leverage float64 - EntryPrice float64 - OpeningAmount float64 - Amount float64 - MarkPrice float64 - PrevMarkPrice float64 - CurrentPrice float64 - CollateralAmount decimal.Decimal - CollateralCurrency currency.Code + CalculateOffline bool + Underlying currency.Code + OrderID string + Asset asset.Item + Side order.Side + Leverage float64 + EntryPrice float64 + OpeningAmount float64 + Amount float64 + MarkPrice float64 + PrevMarkPrice float64 + CurrentPrice float64 } type PNLResult struct { diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index bae0457cb79..f6787fb49d6 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -53,6 +53,7 @@ func parseRaw(t *testing.T, input string) interface{} { Fills: fills, }, }, + CollateralWeightHolder{}, } if err := x.wsHandleData([]byte(input)); err != nil { diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 5005c7952d4..51df927d459 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1288,11 +1288,13 @@ func (f *FTX) ScaleCollateral(calculator exchange.CollateralCalculator) (decimal func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, error) { var result exchange.PNLResult if pnl.CalculateOffline { - collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.CollateralAmount.InexactFloat64(), pnl.EntryPrice, true) - if err != nil { - return nil, err - } - result.Collateral = decimal.NewFromFloat(collat) + // TODO remove this as its not needed here and should be done seperately + //collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.CollateralAmount.InexactFloat64(), pnl.EntryPrice, true) + //if err != nil { + // return nil, err + //} + //result.Collateral = decimal.NewFromFloat(collat) + // TODO add mark price somehow // need mark price candles + mark price from 30 second ago candles uPNL := f.CalculateUnrealisedPNL(pnl.Amount, pnl.MarkPrice, pnl.PrevMarkPrice) diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index efb60f83b40..46b5dd34b1e 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -81,10 +81,8 @@ type IBotExchange interface { GetHistoricCandlesExtended(ctx context.Context, p currency.Pair, a asset.Item, timeStart, timeEnd time.Time, interval kline.Interval) (kline.Item, error) DisableRateLimiter() error EnableRateLimiter() error - CalculatePNL(*PNLCalculator) (*PNLResult, error) - ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) CurrencyStateManagement - + FuturesManagement GetWebsocket() (*stream.Websocket, error) IsWebsocketEnabled() bool SupportsWebsocket() bool @@ -108,3 +106,12 @@ type CurrencyStateManagement interface { CanWithdraw(c currency.Code, a asset.Item) error CanDeposit(c currency.Code, a asset.Item) error } + +type PNLManagement interface { + CalculatePNL(*PNLCalculator) (*PNLResult, error) +} + +type CollateralManagement interface { + ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) + CalculateCollateral(calculator *CollateralCalculator) (decimal.Decimal, error) +} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index df0180a81d6..d28ce4f8259 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -2,10 +2,12 @@ package order import ( "errors" + "fmt" "time" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -19,17 +21,70 @@ func (s Side) IsLong() bool { return s == Long || s == Buy } +func SetupFuturesTracker(exch string, item asset.Item, pair currency.Pair, underlying currency.Code, calculation exchange.PNLManagement) (*FuturesTracker, error) { + if exch == "" { + return nil, errExchangeNameEmpty + } + if !item.IsFutures() { + return nil, errNotFutureAsset + } + if pair.IsEmpty() { + return nil, ErrPairIsEmpty + } + + return &FuturesTracker{ + Exchange: exch, + Asset: item, + ContractPair: pair, + UnderlyingAsset: underlying, + Status: Open, + PNLCalculation: calculation, + }, nil +} + +var errOrderNotEqualToTracker = errors.New("order does not match tracker data") +var errPositionClosed = errors.New("the position is closed, time for a new one") + +func (f *FuturesTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal.Decimal) error { + pnl, err := f.PNLCalculation.CalculatePNL(&exchange.PNLCalculator{ + CalculateOffline: f.OfflinePNLCalculation, + Amount: f.Exposure.InexactFloat64(), + MarkPrice: markPrice.InexactFloat64(), + PrevMarkPrice: prevMarkPrice.InexactFloat64(), + }) + if err != nil { + return err + } + f.UpsertPNLEntry(PNLHistory{ + Time: t, + UnrealisedPNL: pnl.UnrealisedPNL, + }) + return nil +} + // TrackNewOrder knows how things are going for a given // futures contract func (f *FuturesTracker) TrackNewOrder(d *Detail) error { + if f.Status == Closed { + return errPositionClosed + } + if d == nil { + return ErrSubmissionIsNil + } if !f.ContractPair.Equal(d.Pair) { - return errors.New("not the same") + return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, f.ContractPair) } if f.Exchange != d.Exchange { - return errors.New("not the same") + return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, f.Exchange) } if f.Asset != d.AssetType { - return errors.New("not the same") + return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, f.Asset) + } + if d.Side == "" { + return ErrSideIsInvalid + } + if len(f.ShortPositions) == 0 && len(f.LongPositions) == 0 { + f.EntryPrice = decimal.NewFromFloat(d.Price) } for i := range f.ShortPositions { @@ -44,23 +99,38 @@ func (f *FuturesTracker) TrackNewOrder(d *Detail) error { } else { f.LongPositions = append(f.LongPositions, d.Copy()) } - var shortSide, longSide decimal.Decimal + var shortSide, longSide, averageLeverage decimal.Decimal + for i := range f.ShortPositions { shortSide = shortSide.Add(decimal.NewFromFloat(f.ShortPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(f.ShortPositions[i].Leverage) } for i := range f.LongPositions { - longSide = shortSide.Add(decimal.NewFromFloat(f.ShortPositions[i].Amount)) + longSide = longSide.Add(decimal.NewFromFloat(f.LongPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(f.LongPositions[i].Leverage) + } + + averageLeverage.Div(decimal.NewFromInt(int64(len(f.ShortPositions))).Add(decimal.NewFromInt(int64(len(f.LongPositions))))) + + switch { + case longSide.GreaterThan(shortSide): + f.CurrentDirection = Long + case shortSide.GreaterThan(longSide): + f.CurrentDirection = Short + default: + f.CurrentDirection = UnknownSide } if f.CurrentDirection.IsLong() { - f.Exposure = longSide.Sub(shortSide) + f.UnrealisedPNL = longSide.Sub(shortSide) } else { - f.Exposure = shortSide.Sub(longSide) + f.UnrealisedPNL = shortSide.Sub(longSide) } if f.Exposure.Equal(decimal.Zero) { // the order is closed f.Status = Closed - } else { - f.Status = Open + f.ClosingPrice = decimal.NewFromFloat(d.Price) + f.RealisedPNL = f.UnrealisedPNL + f.UnrealisedPNL = decimal.Zero } if f.Exposure.IsNegative() { // tracking here has changed! @@ -70,6 +140,17 @@ func (f *FuturesTracker) TrackNewOrder(d *Detail) error { f.CurrentDirection = Long } } + f.PNLCalculation.CalculatePNL(&exchange.PNLCalculator{ + CalculateOffline: f.OfflinePNLCalculation, + Amount: f.Exposure.InexactFloat64(), + MarkPrice: 0, + PrevMarkPrice: 0, + }) + f.UpsertPNLEntry(PNLHistory{ + Time: time.Time{}, + UnrealisedPNL: decimal.Decimal{}, + RealisedPNL: decimal.Decimal{}, + }) return nil } @@ -88,28 +169,3 @@ func (f *FuturesTracker) UpsertPNLEntry(entry PNLHistory) { } f.PNLHistory = append(f.PNLHistory, entry) } - -// FuturesTracker order is a concept which holds both the opening and closing orders -// for a futures contract. This allows for PNL calculations -type FuturesTracker struct { - Exchange string - Asset asset.Item - ContractPair currency.Pair - UnderlyingAsset currency.Code - Exposure decimal.Decimal - CurrentDirection Side - Status Status - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - ShortPositions []Detail - LongPositions []Detail - PNLHistory []PNLHistory -} - -// PNLHistory tracks how a futures contract -// pnl is going over the history of exposure -type PNLHistory struct { - Time time.Time - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal -} diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go new file mode 100644 index 00000000000..a8f959db506 --- /dev/null +++ b/exchanges/order/futures_test.go @@ -0,0 +1,121 @@ +package order + +import ( + "errors" + "testing" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +func TestTrackNewOrder(t *testing.T) { + exch := "test" + item := asset.Futures + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Error(err) + } + + f, err := SetupFuturesTracker(exch, item, pair, pair.Base) + if !errors.Is(err, nil) { + t.Error(err) + } + + err = f.TrackNewOrder(nil) + if !errors.Is(err, ErrSubmissionIsNil) { + t.Error(err) + } + err = f.TrackNewOrder(&Detail{}) + if !errors.Is(err, errOrderNotEqualToTracker) { + t.Error(err) + } + + od := &Detail{ + Exchange: exch, + AssetType: item, + Pair: pair, + ID: "1", + Price: 1337, + } + err = f.TrackNewOrder(od) + if !errors.Is(err, ErrSideIsInvalid) { + t.Error(err) + } + + od.Side = Long + od.Amount = 1 + od.ID = "2" + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if !f.EntryPrice.Equal(decimal.NewFromInt(1337)) { + t.Errorf("expected 1337, received %v", f.EntryPrice) + } + if len(f.LongPositions) != 1 { + t.Error("expected a long") + } + if f.CurrentDirection != Long { + t.Error("expected recognition that its long") + } + if f.Exposure.InexactFloat64() != od.Amount { + t.Error("expected 1") + } + + od.Amount = 0.4 + od.Side = Short + od.ID = "3" + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(f.ShortPositions) != 1 { + t.Error("expected a short") + } + if f.CurrentDirection != Long { + t.Error("expected recognition that its long") + } + if f.Exposure.InexactFloat64() != 0.6 { + t.Error("expected 0.6") + } + od.Amount = 0.8 + od.Side = Short + od.ID = "4" + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + + if f.CurrentDirection != Short { + t.Error("expected recognition that its short") + } + if !f.Exposure.Equal(decimal.NewFromFloat(0.2)) { + t.Errorf("expected %v received %v", 0.2, f.Exposure) + } + + od.ID = "5" + od.Side = Long + od.Amount = 0.2 + err = f.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Error(err) + } + if f.CurrentDirection != UnknownSide { + t.Error("expected recognition that its unknown") + } + if f.Status != Closed { + t.Error("expected closed position") + } + + err = f.TrackNewOrder(od) + if !errors.Is(err, errPositionClosed) { + t.Error(err) + } + if f.CurrentDirection != UnknownSide { + t.Error("expected recognition that its unknown") + } + if f.Status != Closed { + t.Error("expected closed position") + } +} diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go new file mode 100644 index 00000000000..e153058b33d --- /dev/null +++ b/exchanges/order/futures_types.go @@ -0,0 +1,60 @@ +package order + +import ( + "errors" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +var errExchangeNameEmpty = errors.New("exchange name empty") +var errNotFutureAsset = errors.New("asset type is not futures") + +type ExchangeFuturesTracker struct { + Exchange string + Positions []FuturesTracker + PNL decimal.Decimal + PNLCalculation exchange.PNLManagement + OfflinePNLCalculation bool +} + +type FuturesTrackerSetup struct { + Exchange string + Asset asset.Item + Pair currency.Pair + PNLCalculation exchange.PNLManagement + OfflinePNLCalculation bool +} + +// FuturesTracker order is a concept which holds both the opening and closing orders +// for a futures contract. This allows for PNL calculations +type FuturesTracker struct { + Exchange string + Asset asset.Item + ContractPair currency.Pair + UnderlyingAsset currency.Code + Exposure decimal.Decimal + CurrentDirection Side + Status Status + AverageLeverage decimal.Decimal + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal + ShortPositions []Detail + LongPositions []Detail + PNLHistory []PNLHistory + EntryPrice decimal.Decimal + ClosingPrice decimal.Decimal + OfflinePNLCalculation bool + PNLCalculation exchange.PNLManagement +} + +// PNLHistory tracks how a futures contract +// pnl is going over the history of exposure +type PNLHistory struct { + Time time.Time + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal +} From 8edca3caa071ca24e9b5594b72efff67c6bc264b Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 7 Dec 2021 15:51:29 +1100 Subject: [PATCH 035/171] Fleshes out position tracker to also handle asset and exchange +testing --- .../portfolio/compliance/compliance_types.go | 8 +- .../eventhandlers/portfolio/portfolio.go | 2 +- .../eventhandlers/portfolio/portfolio_test.go | 2 +- .../ftxquarterlyfutures.go | 2 +- engine/order_manager_types.go | 2 +- exchanges/exchange.go | 38 +--- exchanges/ftx/ftx_wrapper.go | 6 +- exchanges/interfaces.go | 15 +- exchanges/order/futures.go | 212 +++++++++++------- exchanges/order/futures_test.go | 187 ++++++++++++++- exchanges/order/futures_types.go | 80 +++++-- 11 files changed, 397 insertions(+), 157 deletions(-) diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index 8ec69cb28e4..9bfef7b0405 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -21,10 +21,10 @@ type Manager struct { // Snapshot consists of the timestamp the snapshot is from, along with all orders made // up until that time type Snapshot struct { - Offset int64 `json:"offset"` - Timestamp time.Time `json:"timestamp"` - Orders []SnapshotOrder `json:"orders"` - FuturesTracker *order.FuturesTracker `json:"futures-order"` + Offset int64 `json:"offset"` + Timestamp time.Time `json:"timestamp"` + Orders []SnapshotOrder `json:"orders"` + FuturesTracker *order.PositionTracker `json:"futures-order"` } // SnapshotOrder adds some additional data that's only relevant for backtesting diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index df1e5eed1e5..ffd01d2e0fe 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -545,7 +545,7 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla } for i := range snapshot.Orders { if snapshot.FuturesTracker == nil { - snapshot.FuturesTracker = &gctorder.FuturesTracker{ + snapshot.FuturesTracker = &gctorder.PositionTracker{ Exchange: e.GetExchange(), Asset: e.GetAssetType(), ContractPair: e.Pair(), diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 147c737e1a5..95394648e12 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -716,7 +716,7 @@ func TestCalculatePNL(t *testing.T) { t.Errorf("received: %v, expected: %v", err, nil) } - futuresOrder := &gctorder.FuturesTracker{ + futuresOrder := &gctorder.PositionTracker{ CurrentDirection: gctorder.Short, ShortPositions: &gctorder.Detail{ Price: 1336, diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go index 937255cc089..7c259facf8d 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go @@ -99,7 +99,7 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol return nil, err } - var o *order.FuturesTracker + var o *order.PositionTracker for i := range currentOrders.Orders { if currentOrders.Orders[i].FuturesOrder != nil { if currentOrders.Orders[i].FuturesOrder.LongPositions == nil { diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index ce496b9d530..d03e3048b96 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -42,7 +42,7 @@ type store struct { commsManager iCommsManager exchangeManager iExchangeManager wg *sync.WaitGroup - futuresTrackers []order.ExchangeFuturesTracker + futuresTrackers map[string]order.ExchangeAssetPositionTracker } // OrderManager processes and stores orders across enabled exchanges diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 4dd80745a49..d03bc9f1e6a 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1462,38 +1462,6 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ return nil, common.ErrFunctionNotSupported } -type CollateralCalculator struct { - CollateralCurrency currency.Code - Asset asset.Item - Side order.Side - CollateralAmount decimal.Decimal - EntryPrice float64 -} - -type PNLCalculator struct { - CalculateOffline bool - Underlying currency.Code - OrderID string - Asset asset.Item - Side order.Side - Leverage float64 - EntryPrice float64 - OpeningAmount float64 - Amount float64 - MarkPrice float64 - PrevMarkPrice float64 - CurrentPrice float64 -} - -type PNLResult struct { - MarginFraction decimal.Decimal - EstimatedLiquidationPrice decimal.Decimal - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - Collateral decimal.Decimal - IsLiquidated bool -} - var ErrUnsetLeverage error // CalculatePNL is an overridable function to allow PNL to be calculated on an @@ -1501,7 +1469,7 @@ var ErrUnsetLeverage error // It will also determine whether the position is considered to be liquidated // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset -func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { +func (b *Base) CalculatePNL(calc *order.PNLCalculator) (*order.PNLResult, error) { if !calc.Asset.IsFutures() { return nil, common.ErrFunctionNotSupported } @@ -1509,7 +1477,7 @@ func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { // you really should have set this earlier, idiot return nil, ErrUnsetLeverage } - var result *PNLResult + var result *order.PNLResult cp := decimal.NewFromFloat(calc.CurrentPrice) op := decimal.NewFromFloat(calc.EntryPrice) lv := decimal.NewFromFloat(calc.Leverage) @@ -1529,7 +1497,7 @@ func (b *Base) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { // It will also determine whether the position is considered to be liquidated // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset -func (b *Base) ScaleCollateral(calc *CollateralCalculator) (decimal.Decimal, error) { +func (b *Base) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { if !calc.Asset.IsFutures() { return decimal.Zero, common.ErrFunctionNotSupported } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 51df927d459..bfb57ade9a8 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1276,7 +1276,7 @@ func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice floa return positionSize * (markPrice - prevMarkPrice) } -func (f *FTX) ScaleCollateral(calculator exchange.CollateralCalculator) (decimal.Decimal, error) { +func (f *FTX) ScaleCollateral(calculator order.CollateralCalculator) (decimal.Decimal, error) { collat, err := f.CalculateCollateral(calculator.CollateralCurrency, calculator.CollateralAmount.InexactFloat64(), calculator.EntryPrice, true) if err != nil { return decimal.Zero, err @@ -1285,8 +1285,8 @@ func (f *FTX) ScaleCollateral(calculator exchange.CollateralCalculator) (decimal return decimal.NewFromFloat(collat), nil } -func (f *FTX) CalculatePNL(pnl *exchange.PNLCalculator) (*exchange.PNLResult, error) { - var result exchange.PNLResult +func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { + var result order.PNLResult if pnl.CalculateOffline { // TODO remove this as its not needed here and should be done seperately //collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.CollateralAmount.InexactFloat64(), pnl.EntryPrice, true) diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index 46b5dd34b1e..30d26f52646 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -5,7 +5,6 @@ import ( "sync" "time" - "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/account" @@ -82,7 +81,10 @@ type IBotExchange interface { DisableRateLimiter() error EnableRateLimiter() error CurrencyStateManagement - FuturesManagement + + order.PNLManagement + order.CollateralManagement + GetWebsocket() (*stream.Websocket, error) IsWebsocketEnabled() bool SupportsWebsocket() bool @@ -106,12 +108,3 @@ type CurrencyStateManagement interface { CanWithdraw(c currency.Code, a asset.Item) error CanDeposit(c currency.Code, a asset.Item) error } - -type PNLManagement interface { - CalculatePNL(*PNLCalculator) (*PNLResult, error) -} - -type CollateralManagement interface { - ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) - CalculateCollateral(calculator *CollateralCalculator) (decimal.Decimal, error) -} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index d28ce4f8259..9069e5305f6 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -3,169 +3,217 @@ package order import ( "errors" "fmt" + "strings" "time" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) -// IsShort returns if the side is short -func (s Side) IsShort() bool { - return s == Short || s == Sell +// SetupExchangeAssetPositionTracker creates a futures order tracker for a specific exchange +func SetupExchangeAssetPositionTracker(exch string, item asset.Item, offlineCalculation bool, calculation PNLManagement) (*ExchangeAssetPositionTracker, error) { + if exch == "" { + return nil, errExchangeNameEmpty + } + if !item.IsValid() || !item.IsFutures() { + return nil, errNotFutureAsset + } + if calculation == nil { + return nil, errMissingPNLCalculationFunctions + } + return &ExchangeAssetPositionTracker{ + Exchange: strings.ToLower(exch), + Asset: item, + PNLCalculation: calculation, + OfflinePNLCalculation: offlineCalculation, + }, nil } -// IsLong returns if the side is long -func (s Side) IsLong() bool { - return s == Long || s == Buy +// TrackNewOrder upserts an order to the tracker and updates position +// status and exposure. PNL is calculated separately as it requires mark prices +func (e *ExchangeAssetPositionTracker) TrackNewOrder(d *Detail) error { + if d == nil { + return ErrSubmissionIsNil + } + if d.AssetType != e.Asset { + return errAssetMismatch + } + if len(e.Positions) > 0 { + for i := range e.Positions { + if e.Positions[i].Status == Open && i != len(e.Positions)-1 { + return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.Positions[i], i, len(e.Positions)-1) + } + } + err := e.Positions[len(e.Positions)-1].TrackNewOrder(d) + if !errors.Is(err, errPositionClosed) { + return err + } + } + tracker, err := e.SetupPositionTracker(d.AssetType, d.Pair, d.Pair.Base) + if err != nil { + return err + } + e.Positions = append(e.Positions, tracker) + + return tracker.TrackNewOrder(d) } -func SetupFuturesTracker(exch string, item asset.Item, pair currency.Pair, underlying currency.Code, calculation exchange.PNLManagement) (*FuturesTracker, error) { - if exch == "" { +// SetupPositionTracker creates a new position tracker to track n futures orders +// until the position(s) are closed +func (e *ExchangeAssetPositionTracker) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code) (*PositionTracker, error) { + if e.Exchange == "" { return nil, errExchangeNameEmpty } - if !item.IsFutures() { + if e.PNLCalculation == nil { + return nil, errMissingPNLCalculationFunctions + } + if !item.IsValid() || !item.IsFutures() { return nil, errNotFutureAsset } if pair.IsEmpty() { return nil, ErrPairIsEmpty } - return &FuturesTracker{ - Exchange: exch, - Asset: item, - ContractPair: pair, - UnderlyingAsset: underlying, - Status: Open, - PNLCalculation: calculation, + return &PositionTracker{ + Exchange: strings.ToLower(e.Exchange), + Asset: item, + ContractPair: pair, + UnderlyingAsset: underlying, + Status: Open, + PNLCalculation: e.PNLCalculation, + OfflinePNLCalculation: e.OfflinePNLCalculation, }, nil } -var errOrderNotEqualToTracker = errors.New("order does not match tracker data") -var errPositionClosed = errors.New("the position is closed, time for a new one") - -func (f *FuturesTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal.Decimal) error { - pnl, err := f.PNLCalculation.CalculatePNL(&exchange.PNLCalculator{ - CalculateOffline: f.OfflinePNLCalculation, - Amount: f.Exposure.InexactFloat64(), +// TrackPNL calculates the PNL based on a position tracker's exposure +// and current pricing. Adds the entry to PNL history to track over time +func (p *PositionTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal.Decimal) error { + pnl, err := p.PNLCalculation.CalculatePNL(&PNLCalculator{ + CalculateOffline: p.OfflinePNLCalculation, + Amount: p.Exposure.InexactFloat64(), MarkPrice: markPrice.InexactFloat64(), PrevMarkPrice: prevMarkPrice.InexactFloat64(), }) if err != nil { return err } - f.UpsertPNLEntry(PNLHistory{ + return p.UpsertPNLEntry(PNLHistory{ Time: t, UnrealisedPNL: pnl.UnrealisedPNL, }) - return nil } // TrackNewOrder knows how things are going for a given // futures contract -func (f *FuturesTracker) TrackNewOrder(d *Detail) error { - if f.Status == Closed { +func (p *PositionTracker) TrackNewOrder(d *Detail) error { + if p.Status == Closed { return errPositionClosed } if d == nil { return ErrSubmissionIsNil } - if !f.ContractPair.Equal(d.Pair) { - return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, f.ContractPair) + if !p.ContractPair.Equal(d.Pair) { + return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.ContractPair) } - if f.Exchange != d.Exchange { - return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, f.Exchange) + if p.Exchange != strings.ToLower(d.Exchange) { + return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.Exchange) } - if f.Asset != d.AssetType { - return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, f.Asset) + if p.Asset != d.AssetType { + return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.Asset) } if d.Side == "" { return ErrSideIsInvalid } - if len(f.ShortPositions) == 0 && len(f.LongPositions) == 0 { - f.EntryPrice = decimal.NewFromFloat(d.Price) + if d.ID == "" { + return ErrOrderIDNotSet + } + if len(p.ShortPositions) == 0 && len(p.LongPositions) == 0 { + p.EntryPrice = decimal.NewFromFloat(d.Price) } - for i := range f.ShortPositions { - if f.ShortPositions[i].ID == d.ID { - f.ShortPositions[i] = d.Copy() + for i := range p.ShortPositions { + if p.ShortPositions[i].ID == d.ID { + // update, not overwrite + ord := p.ShortPositions[i].Copy() + ord.UpdateOrderFromDetail(d) + p.ShortPositions[i] = ord break } } if d.Side.IsShort() { - f.ShortPositions = append(f.ShortPositions, d.Copy()) + p.ShortPositions = append(p.ShortPositions, d.Copy()) } else { - f.LongPositions = append(f.LongPositions, d.Copy()) + p.LongPositions = append(p.LongPositions, d.Copy()) } var shortSide, longSide, averageLeverage decimal.Decimal - for i := range f.ShortPositions { - shortSide = shortSide.Add(decimal.NewFromFloat(f.ShortPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(f.ShortPositions[i].Leverage) + for i := range p.ShortPositions { + shortSide = shortSide.Add(decimal.NewFromFloat(p.ShortPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(p.ShortPositions[i].Leverage) } - for i := range f.LongPositions { - longSide = longSide.Add(decimal.NewFromFloat(f.LongPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(f.LongPositions[i].Leverage) + for i := range p.LongPositions { + longSide = longSide.Add(decimal.NewFromFloat(p.LongPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(p.LongPositions[i].Leverage) } - averageLeverage.Div(decimal.NewFromInt(int64(len(f.ShortPositions))).Add(decimal.NewFromInt(int64(len(f.LongPositions))))) + averageLeverage.Div(decimal.NewFromInt(int64(len(p.ShortPositions))).Add(decimal.NewFromInt(int64(len(p.LongPositions))))) switch { case longSide.GreaterThan(shortSide): - f.CurrentDirection = Long + p.CurrentDirection = Long case shortSide.GreaterThan(longSide): - f.CurrentDirection = Short + p.CurrentDirection = Short default: - f.CurrentDirection = UnknownSide + p.CurrentDirection = UnknownSide } - if f.CurrentDirection.IsLong() { - f.UnrealisedPNL = longSide.Sub(shortSide) + if p.CurrentDirection.IsLong() { + p.Exposure = longSide.Sub(shortSide) } else { - f.UnrealisedPNL = shortSide.Sub(longSide) + p.Exposure = shortSide.Sub(longSide) } - if f.Exposure.Equal(decimal.Zero) { + if p.Exposure.Equal(decimal.Zero) { // the order is closed - f.Status = Closed - f.ClosingPrice = decimal.NewFromFloat(d.Price) - f.RealisedPNL = f.UnrealisedPNL - f.UnrealisedPNL = decimal.Zero + p.Status = Closed + p.ClosingPrice = decimal.NewFromFloat(d.Price) + p.RealisedPNL = p.UnrealisedPNL + p.UnrealisedPNL = decimal.Zero } - if f.Exposure.IsNegative() { + if p.Exposure.IsNegative() { // tracking here has changed! - if f.CurrentDirection.IsLong() { - f.CurrentDirection = Short + if p.CurrentDirection.IsLong() { + p.CurrentDirection = Short } else { - f.CurrentDirection = Long + p.CurrentDirection = Long } + p.Exposure = p.Exposure.Abs() } - f.PNLCalculation.CalculatePNL(&exchange.PNLCalculator{ - CalculateOffline: f.OfflinePNLCalculation, - Amount: f.Exposure.InexactFloat64(), - MarkPrice: 0, - PrevMarkPrice: 0, - }) - f.UpsertPNLEntry(PNLHistory{ - Time: time.Time{}, - UnrealisedPNL: decimal.Decimal{}, - RealisedPNL: decimal.Decimal{}, - }) - return nil } // UpsertPNLEntry upserts an entry to PNLHistory field // with some basic checks -func (f *FuturesTracker) UpsertPNLEntry(entry PNLHistory) { +func (p *PositionTracker) UpsertPNLEntry(entry PNLHistory) error { if entry.Time.IsZero() { - return + return errTimeUnset } - for i := range f.PNLHistory { - if entry.Time.Equal(f.PNLHistory[i].Time) { - f.PNLHistory[i] = entry - return + for i := range p.PNLHistory { + if entry.Time.Equal(p.PNLHistory[i].Time) { + p.PNLHistory[i] = entry + return nil } } - f.PNLHistory = append(f.PNLHistory, entry) + p.PNLHistory = append(p.PNLHistory, entry) + return nil +} + +// IsShort returns if the side is short +func (s Side) IsShort() bool { + return s == Short || s == Sell +} + +// IsLong returns if the side is long +func (s Side) IsLong() bool { + return s == Long || s == Buy } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index a8f959db506..1e4c16f55d9 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -3,21 +3,93 @@ package order import ( "errors" "testing" + "time" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) -func TestTrackNewOrder(t *testing.T) { +type FakePNL struct { + err error + result *PNLResult +} + +func (f *FakePNL) CalculatePNL(*PNLCalculator) (*PNLResult, error) { + if f.err != nil { + return nil, f.err + } + return f.result, nil +} + +func TestTrackPNL(t *testing.T) { + t.Parallel() exch := "test" item := asset.Futures pair, err := currency.NewPairFromStrings("BTC", "1231") if !errors.Is(err, nil) { t.Error(err) } + fPNL := &FakePNL{ + result: &PNLResult{}, + } + e := ExchangeAssetPositionTracker{ + Exchange: exch, + PNLCalculation: fPNL, + } + f, err := e.SetupPositionTracker(item, pair, pair.Base) + if !errors.Is(err, nil) { + t.Error(err) + } + err = f.TrackPNL(time.Now(), decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Error(err) + } + fPNL.err = errMissingPNLCalculationFunctions + err = f.TrackPNL(time.Now(), decimal.Zero, decimal.Zero) + if !errors.Is(err, errMissingPNLCalculationFunctions) { + t.Error(err) + } +} - f, err := SetupFuturesTracker(exch, item, pair, pair.Base) +func TestUpsertPNLEntry(t *testing.T) { + t.Parallel() + f := &PositionTracker{} + err := f.UpsertPNLEntry(PNLHistory{}) + if !errors.Is(err, errTimeUnset) { + t.Error(err) + } + tt := time.Now() + err = f.UpsertPNLEntry(PNLHistory{Time: tt}) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(f.PNLHistory) != 1 { + t.Errorf("expected 1 received %v", len(f.PNLHistory)) + } + + err = f.UpsertPNLEntry(PNLHistory{Time: tt}) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(f.PNLHistory) != 1 { + t.Errorf("expected 1 received %v", len(f.PNLHistory)) + } +} + +func TestTrackNewOrder(t *testing.T) { + t.Parallel() + exch := "test" + item := asset.Futures + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Error(err) + } + e := ExchangeAssetPositionTracker{ + Exchange: "test", + PNLCalculation: &FakePNL{}, + } + f, err := e.SetupPositionTracker(item, pair, pair.Base) if !errors.Is(err, nil) { t.Error(err) } @@ -119,3 +191,114 @@ func TestTrackNewOrder(t *testing.T) { t.Error("expected closed position") } } + +func TestSetupFuturesTracker(t *testing.T) { + t.Parallel() + _, err := SetupExchangeAssetPositionTracker("", "", false, nil) + if !errors.Is(err, errExchangeNameEmpty) { + t.Error(err) + } + + _, err = SetupExchangeAssetPositionTracker("test", "", false, nil) + if !errors.Is(err, errNotFutureAsset) { + t.Error(err) + } + + _, err = SetupExchangeAssetPositionTracker("test", asset.Futures, false, nil) + if !errors.Is(err, errMissingPNLCalculationFunctions) { + t.Error(err) + } + + resp, err := SetupExchangeAssetPositionTracker("test", asset.Futures, false, &FakePNL{}) + if !errors.Is(err, nil) { + t.Error(err) + } + if resp.Exchange != "test" { + t.Errorf("expected 'test' received %v", resp.Exchange) + } +} + +func TestExchangeTrackNewOrder(t *testing.T) { + t.Parallel() + resp, err := SetupExchangeAssetPositionTracker("test", asset.Futures, false, &FakePNL{}) + if !errors.Is(err, nil) { + t.Error(err) + } + err = resp.TrackNewOrder(&Detail{ + Exchange: "test", + AssetType: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Side: Short, + ID: "1", + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(resp.Positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.Positions)) + } + + err = resp.TrackNewOrder(&Detail{ + Exchange: "test", + AssetType: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Side: Short, + ID: "1", + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(resp.Positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.Positions)) + } + + err = resp.TrackNewOrder(&Detail{ + Exchange: "test", + AssetType: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Side: Long, + ID: "2", + Amount: 2, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if len(resp.Positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.Positions)) + } + if resp.Positions[0].Status != Closed { + t.Errorf("expected 'closed' received %v", resp.Positions[0].Status) + } + resp.Positions[0].Status = Open + resp.Positions = append(resp.Positions, resp.Positions...) + err = resp.TrackNewOrder(&Detail{ + Exchange: "test", + AssetType: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Side: Long, + ID: "2", + Amount: 2, + }) + if !errors.Is(err, errPositionDiscrepancy) { + t.Error(err) + } + if len(resp.Positions) != 2 { + t.Errorf("expected '2' received %v", len(resp.Positions)) + } + + resp.Positions[0].Status = Closed + err = resp.TrackNewOrder(&Detail{ + Exchange: "test", + AssetType: asset.USDTMarginedFutures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Side: Long, + ID: "2", + Amount: 2, + }) + if !errors.Is(err, errAssetMismatch) { + t.Error(err) + } + +} diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index e153058b33d..5e9bfef5020 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -6,32 +6,80 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) -var errExchangeNameEmpty = errors.New("exchange name empty") -var errNotFutureAsset = errors.New("asset type is not futures") +var ( + errExchangeNameEmpty = errors.New("exchange name empty") + errNotFutureAsset = errors.New("asset type is not futures") + errTimeUnset = errors.New("time unset") + errMissingPNLCalculationFunctions = errors.New("futures tracker requires exchange PNL calculation functions") + errOrderNotEqualToTracker = errors.New("order does not match tracker data") + errPositionClosed = errors.New("the position is closed, time for a new one") + errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") + errAssetMismatch = errors.New("provided asset does not match") +) -type ExchangeFuturesTracker struct { - Exchange string - Positions []FuturesTracker - PNL decimal.Decimal - PNLCalculation exchange.PNLManagement - OfflinePNLCalculation bool +type PNLManagement interface { + CalculatePNL(*PNLCalculator) (*PNLResult, error) } -type FuturesTrackerSetup struct { +type CollateralManagement interface { + ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) + CalculateCollateral(calculator *CollateralCalculator) (decimal.Decimal, error) +} + +type CollateralCalculator struct { + CollateralCurrency currency.Code + Asset asset.Item + Side Side + CollateralAmount decimal.Decimal + EntryPrice float64 +} + +type PNLCalculator struct { + CalculateOffline bool + Underlying currency.Code + OrderID string + Asset asset.Item + Side Side + Leverage float64 + EntryPrice float64 + OpeningAmount float64 + Amount float64 + MarkPrice float64 + PrevMarkPrice float64 + CurrentPrice float64 +} + +type PNLResult struct { + MarginFraction decimal.Decimal + EstimatedLiquidationPrice decimal.Decimal + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal + Collateral decimal.Decimal + IsLiquidated bool +} + +// ExchangeAssetPositionTracker will track the performance of +// po +type ExchangeAssetPositionTracker struct { Exchange string Asset asset.Item - Pair currency.Pair - PNLCalculation exchange.PNLManagement + Positions []*PositionTracker + PNL decimal.Decimal + PNLCalculation PNLManagement OfflinePNLCalculation bool } -// FuturesTracker order is a concept which holds both the opening and closing orders -// for a futures contract. This allows for PNL calculations -type FuturesTracker struct { +// PositionTracker tracks futures orders until the overall position is considered closed +// eg a user can open a short position, append to it via two more shorts, reduce via a small long and +// finally close off the remainder via another long. All of these actions are to be +// captured within one position tracker. It allows for a user to understand their PNL +// specifically for futures positions. Utilising spot/futures arbitrage will not be tracked +// completely within this position tracker, however, can still provide a good +// timeline of performance until the position is closed +type PositionTracker struct { Exchange string Asset asset.Item ContractPair currency.Pair @@ -48,7 +96,7 @@ type FuturesTracker struct { EntryPrice decimal.Decimal ClosingPrice decimal.Decimal OfflinePNLCalculation bool - PNLCalculation exchange.PNLManagement + PNLCalculation PNLManagement } // PNLHistory tracks how a futures contract From 676d51e1d9a7915dffcfa12d842929d317567d98 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 8 Dec 2021 16:20:12 +1100 Subject: [PATCH 036/171] RM linkedOrderID. rn positioncontroller, unexport --- backtester/eventhandlers/exchange/exchange.go | 20 +- .../portfolio/compliance/compliance.go | 14 +- .../portfolio/compliance/compliance_types.go | 8 +- .../eventhandlers/portfolio/portfolio.go | 67 +++---- backtester/eventtypes/fill/fill.go | 6 - backtester/eventtypes/order/order.go | 6 - backtester/eventtypes/order/order_types.go | 6 - engine/order_manager_types.go | 2 +- exchanges/exchange.go | 31 +-- exchanges/ftx/ftx.go | 15 -- exchanges/ftx/ftx_wrapper.go | 49 +++-- exchanges/order/futures.go | 177 ++++++++++-------- exchanges/order/futures_test.go | 123 ++++++------ exchanges/order/futures_types.go | 70 ++++--- 14 files changed, 277 insertions(+), 317 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 862f0fff580..c10cbea450a 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -41,7 +41,6 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * Direction: o.GetDirection(), Amount: o.GetAmount(), ClosePrice: data.Latest().GetClosePrice(), - LinkedOrderID: o.GetLinkedOrderID(), } eventFunds := o.GetAllocatedFunds() cs, err := e.GetCurrencySettings(o.GetExchange(), o.GetAssetType(), o.Pair()) @@ -154,20 +153,11 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if fundErr != nil { return f, fundErr } - if o.GetLinkedOrderID() != "" { - switch f.GetDirection() { - case gctorder.Short: - f.SetDirection(common.CouldNotCloseLong) - case gctorder.Long: - f.SetDirection(common.CouldNotCloseShort) - } - } else { - switch f.GetDirection() { - case gctorder.Short: - f.SetDirection(common.CouldNotShort) - case gctorder.Long: - f.SetDirection(common.CouldNotLong) - } + switch f.GetDirection() { + case gctorder.Short: + f.SetDirection(common.CouldNotShort) + case gctorder.Long: + f.SetDirection(common.CouldNotLong) } return f, err } diff --git a/backtester/eventhandlers/portfolio/compliance/compliance.go b/backtester/eventhandlers/portfolio/compliance/compliance.go index 08e7fec05ba..aadfd92fdff 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance.go @@ -7,24 +7,20 @@ import ( // AddSnapshot creates a snapshot in time of the orders placed to allow for finer detail tracking // and to protect against anything modifying order details elsewhere -func (m *Manager) AddSnapshot(orders []SnapshotOrder, t time.Time, offset int64, overwriteExisting bool) error { +func (m *Manager) AddSnapshot(snap *Snapshot, overwriteExisting bool) error { if overwriteExisting { if len(m.Snapshots) == 0 { return errSnapshotNotFound } for i := len(m.Snapshots) - 1; i >= 0; i-- { - if offset == m.Snapshots[i].Offset { - m.Snapshots[i].Orders = orders + if snap.Offset == m.Snapshots[i].Offset { + m.Snapshots[i].Orders = snap.Orders return nil } } - return fmt.Errorf("%w at %v", errSnapshotNotFound, offset) + return fmt.Errorf("%w at %v", errSnapshotNotFound, snap.Offset) } - m.Snapshots = append(m.Snapshots, Snapshot{ - Orders: orders, - Timestamp: t, - Offset: offset, - }) + m.Snapshots = append(m.Snapshots, *snap) return nil } diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index 9bfef7b0405..4a811ebea30 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -21,10 +21,10 @@ type Manager struct { // Snapshot consists of the timestamp the snapshot is from, along with all orders made // up until that time type Snapshot struct { - Offset int64 `json:"offset"` - Timestamp time.Time `json:"timestamp"` - Orders []SnapshotOrder `json:"orders"` - FuturesTracker *order.PositionTracker `json:"futures-order"` + Offset int64 `json:"offset"` + Timestamp time.Time `json:"timestamp"` + Orders []SnapshotOrder `json:"orders"` + FuturesTracker *order.PositionController `json:"futures-tracker"` } // SnapshotOrder adds some additional data that's only relevant for backtesting diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index ffd01d2e0fe..6b77e87e856 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -135,12 +135,12 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return nil, err } sizingFunds = cReader.AvailableFunds() - sizingFunds, err = lookup.Exchange.ScaleCollateral(&gctexchange.CollateralCalculator{ + sizingFunds, err = lookup.Exchange.ScaleCollateral(&gctorder.CollateralCalculator{ CollateralCurrency: cReader.CollateralCurrency(), Asset: ev.GetAssetType(), Side: ev.GetDirection(), CollateralAmount: sizingFunds, - EntryPrice: ev.GetPrice().InexactFloat64(), + USDPrice: ev.GetPrice(), }) if err != nil { return nil, err @@ -341,7 +341,12 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { } } } - return complianceManager.AddSnapshot(prevSnap.Orders, fillEvent.GetTime(), fillEvent.GetOffset(), false) + snap := &compliance.Snapshot{ + Offset: fillEvent.GetOffset(), + Timestamp: fillEvent.GetTime(), + Orders: prevSnap.Orders, + } + return complianceManager.AddSnapshot(snap, false) } // GetComplianceManager returns the order snapshots for a given exchange, asset, pair @@ -543,52 +548,32 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla if err != nil { return err } - for i := range snapshot.Orders { - if snapshot.FuturesTracker == nil { - snapshot.FuturesTracker = &gctorder.PositionTracker{ - Exchange: e.GetExchange(), - Asset: e.GetAssetType(), - ContractPair: e.Pair(), - UnderlyingAsset: funds.UnderlyingAsset(), - } - } - - // should futures tracker reference the interface function? - // eg it can call calculate pnl via using the exchange's implementation? - snapshot.FuturesTracker.TrackNewOrder() - var result *gctexchange.PNLResult - result, err = settings.Exchange.CalculatePNL(&gctexchange.PNLCalculator{ + if snapshot.FuturesTracker == nil { + snapshot.FuturesTracker, err = gctorder.SetupPositionController(&gctorder.PositionControllerSetup{ + Exchange: e.GetExchange(), Asset: e.GetAssetType(), - Leverage: snapshot.Orders[i].FuturesOrder.ShortPositions.Leverage, - EntryPrice: snapshot.Orders[i].FuturesOrder.ShortPositions.Price, - OpeningAmount: snapshot.Orders[i].FuturesOrder.ShortPositions.Amount, - CurrentPrice: e.GetClosePrice().InexactFloat64(), - CollateralAmount: funds.AvailableFunds(), - CalculateOffline: true, - CollateralCurrency: funds.CollateralCurrency(), - Amount: snapshot.Orders[i].FuturesOrder.ShortPositions.Amount, - MarkPrice: e.GetClosePrice().InexactFloat64(), - PrevMarkPrice: e.GetOpenPrice().InexactFloat64(), + Pair: e.Pair(), + Underlying: e.Pair().Base, + OfflineCalculation: true, + PNLCalculator: settings.Exchange, }) if err != nil { return err } + } - if result.IsLiquidated { - funds.Liquidate() - snapshot.Orders[i].FuturesOrder.UnrealisedPNL = decimal.Zero - snapshot.Orders[i].FuturesOrder.RealisedPNL = decimal.Zero - snapshot.Orders[i].FuturesOrder.CurrentDirection = common.Liquidated - return nil + for i := range snapshot.Orders { + err = snapshot.FuturesTracker.TrackNewOrder(snapshot.Orders[i].Order) + if err != nil { + return err } - snapshot.Orders[i].FuturesOrder.RealisedPNL.Add(snapshot.Orders[i].FuturesOrder.UnrealisedPNL) - snapshot.Orders[i].FuturesOrder.UnrealisedPNL = result.UnrealisedPNL - snapshot.Orders[i].FuturesOrder.UpsertPNLEntry(gctorder.PNLHistory{ - Time: e.GetTime(), - UnrealisedPNL: result.UnrealisedPNL, - RealisedPNL: snapshot.Orders[i].FuturesOrder.RealisedPNL, - }) } + + _, err = snapshot.FuturesTracker.CalculateLatestPNL(&gctorder.PNLCalculator{ + MarkPrice: e.GetClosePrice().InexactFloat64(), + PrevMarkPrice: e.GetOpenPrice().InexactFloat64(), + }) + return nil } diff --git a/backtester/eventtypes/fill/fill.go b/backtester/eventtypes/fill/fill.go index 61d2d333fa7..609c6225ced 100644 --- a/backtester/eventtypes/fill/fill.go +++ b/backtester/eventtypes/fill/fill.go @@ -64,9 +64,3 @@ func (f *Fill) GetOrder() *order.Detail { func (f *Fill) GetSlippageRate() decimal.Decimal { return f.Slippage } - -// GetLinkedOrderID returns the order ID of a linked -// futures order -func (f *Fill) GetLinkedOrderID() string { - return f.LinkedOrderID -} diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index 47143955057..102ba10ad84 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -81,9 +81,3 @@ func (o *Order) SetLeverage(l decimal.Decimal) { func (o *Order) GetAllocatedFunds() decimal.Decimal { return o.AllocatedFunds } - -// GetLinkedOrderID returns the order ID of a linked -// futures order -func (o *Order) GetLinkedOrderID() string { - return o.LinkedOrderID -} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index 7cf43206404..4f7c6aff3ff 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -20,11 +20,6 @@ type Order struct { AllocatedFunds decimal.Decimal BuyLimit decimal.Decimal SellLimit decimal.Decimal - // LinkedOrderID is the order ID of the futures order - // to that is being closed. This linking allows the - // more detailed order.Futures struct to close out - // and have more detailed performance tracking - LinkedOrderID string } // Event inherits common event interfaces along with extra functions related to handling orders @@ -41,5 +36,4 @@ type Event interface { GetID() string IsLeveraged() bool GetAllocatedFunds() decimal.Decimal - GetLinkedOrderID() string } diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index d03e3048b96..3f12d82533d 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -42,7 +42,7 @@ type store struct { commsManager iCommsManager exchangeManager iExchangeManager wg *sync.WaitGroup - futuresTrackers map[string]order.ExchangeAssetPositionTracker + futuresTrackers map[string]order.PositionController } // OrderManager processes and stores orders across enabled exchanges diff --git a/exchanges/exchange.go b/exchanges/exchange.go index d03bc9f1e6a..fe9e617b3b2 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1462,34 +1462,13 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ return nil, common.ErrFunctionNotSupported } -var ErrUnsetLeverage error - // CalculatePNL is an overridable function to allow PNL to be calculated on an // open position // It will also determine whether the position is considered to be liquidated // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset func (b *Base) CalculatePNL(calc *order.PNLCalculator) (*order.PNLResult, error) { - if !calc.Asset.IsFutures() { - return nil, common.ErrFunctionNotSupported - } - if calc.Leverage == 0 { - // you really should have set this earlier, idiot - return nil, ErrUnsetLeverage - } - var result *order.PNLResult - cp := decimal.NewFromFloat(calc.CurrentPrice) - op := decimal.NewFromFloat(calc.EntryPrice) - lv := decimal.NewFromFloat(calc.Leverage) - result.UnrealisedPNL = cp.Sub(op).Mul(op).Mul(lv) - if result.UnrealisedPNL.IsNegative() && calc.CollateralAmount.LessThanOrEqual(result.UnrealisedPNL.Abs()) { - // calculating whether something is liquidated changes per exchange - // If your chosen exchange has its own liquidation formula, please ensure - // it is implemented there rather than rely on this base function - result.IsLiquidated = true - } - - return result, nil + return nil, common.ErrFunctionNotSupported } // ScaleCollateral is an overridable function to allow PNL to be calculated on an @@ -1498,9 +1477,9 @@ func (b *Base) CalculatePNL(calc *order.PNLCalculator) (*order.PNLResult, error) // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset func (b *Base) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { - if !calc.Asset.IsFutures() { - return decimal.Zero, common.ErrFunctionNotSupported - } + return decimal.Zero, common.ErrFunctionNotSupported +} - return calc.CollateralAmount, nil +func (b *Base) CalculateTotalCollateral([]order.CollateralCalculator) (decimal.Decimal, error) { + return decimal.Zero, common.ErrFunctionNotSupported } diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 084194a3211..a2d971abe02 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1470,21 +1470,6 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err return limits, nil } -func (f *FTX) CalculateCollateral(code currency.Code, amount, price float64, isPositiveBalance bool) (float64, error) { - collateralWeight, ok := f.collateralWeight[code.Upper().String()] - if !ok { - return 0, errCoinMustBeSpecified - } - if isPositiveBalance { - if collateralWeight.IMFFactor == 0 { - return 0, errCoinMustBeSpecified - } - return amount * price * math.Min(collateralWeight.Total, 1.1/collateralWeight.IMFFactor*math.Sqrt(amount)), nil - } - // im not sure this is what FTX means - return amount * price, nil -} - func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64, side order.Side) (float64, error) { collateralWeight, ok := f.collateralWeight[code.Upper().String()] if !ok { diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index bfb57ade9a8..e3181688828 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "sort" "strconv" "strings" @@ -1276,27 +1277,9 @@ func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice floa return positionSize * (markPrice - prevMarkPrice) } -func (f *FTX) ScaleCollateral(calculator order.CollateralCalculator) (decimal.Decimal, error) { - collat, err := f.CalculateCollateral(calculator.CollateralCurrency, calculator.CollateralAmount.InexactFloat64(), calculator.EntryPrice, true) - if err != nil { - return decimal.Zero, err - } - - return decimal.NewFromFloat(collat), nil -} - func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { var result order.PNLResult if pnl.CalculateOffline { - // TODO remove this as its not needed here and should be done seperately - //collat, err := f.CalculateCollateral(pnl.CollateralCurrency, pnl.CollateralAmount.InexactFloat64(), pnl.EntryPrice, true) - //if err != nil { - // return nil, err - //} - //result.Collateral = decimal.NewFromFloat(collat) - - // TODO add mark price somehow - // need mark price candles + mark price from 30 second ago candles uPNL := f.CalculateUnrealisedPNL(pnl.Amount, pnl.MarkPrice, pnl.PrevMarkPrice) result.UnrealisedPNL = decimal.NewFromFloat(uPNL) return &result, nil @@ -1324,15 +1307,31 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { if info.Positions[i].EntryPrice == pnl.EntryPrice && pnl.Side == pnlSide { result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPnL) result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPnL) - var collat float64 - collat, err = f.CalculateCollateral(pnl.Underlying, info.Positions[i].Size, info.Positions[i].EntryPrice, result.UnrealisedPNL.IsPositive()) - if err != nil { - return nil, err - } - result.Collateral = decimal.NewFromFloat(collat) return &result, nil } } } - return nil, nil + return &result, nil +} + +func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalculator) (decimal.Decimal, error) { + var result decimal.Decimal + for i := range collateralAssets { + collateralWeight, ok := f.collateralWeight[collateralAssets[i].CollateralCurrency.Upper().String()] + if !ok { + return decimal.Zero, errCoinMustBeSpecified + } + if collateralAssets[i].CollateralAmount.IsPositive() { + if collateralWeight.IMFFactor == 0 { + return decimal.Zero, errCoinMustBeSpecified + } + what := decimal.NewFromFloat(collateralWeight.Total) + what2 := decimal.NewFromFloat(1.1 / collateralWeight.IMFFactor * math.Sqrt(collateralAssets[i].CollateralAmount.InexactFloat64())) + result = result.Add(collateralAssets[i].CollateralAmount.Mul(collateralAssets[i].USDPrice).Mul(decimal.Min(what, what2))) + + } else { + result = result.Add(collateralAssets[i].CollateralAmount.Mul(collateralAssets[i].USDPrice)) + } + } + return result, nil } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 9069e5305f6..e3eba330cf8 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -11,41 +11,55 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) -// SetupExchangeAssetPositionTracker creates a futures order tracker for a specific exchange -func SetupExchangeAssetPositionTracker(exch string, item asset.Item, offlineCalculation bool, calculation PNLManagement) (*ExchangeAssetPositionTracker, error) { - if exch == "" { +// SetupPositionController creates a futures order tracker for a specific exchange +func SetupPositionController(setup *PositionControllerSetup) (*PositionController, error) { + if setup.Exchange == "" { return nil, errExchangeNameEmpty } - if !item.IsValid() || !item.IsFutures() { + if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { return nil, errNotFutureAsset } - if calculation == nil { + if setup.Pair.IsEmpty() { + return nil, ErrPairIsEmpty + } + if setup.Underlying.IsEmpty() { + return nil, errEmptyUnderlying + } + if setup.PNLCalculator == nil { return nil, errMissingPNLCalculationFunctions } - return &ExchangeAssetPositionTracker{ - Exchange: strings.ToLower(exch), - Asset: item, - PNLCalculation: calculation, - OfflinePNLCalculation: offlineCalculation, + return &PositionController{ + exchange: strings.ToLower(setup.Exchange), + asset: setup.Asset, + pair: setup.Pair, + underlying: setup.Underlying, + pnlCalculation: setup.PNLCalculator, + offlinePNLCalculation: setup.OfflineCalculation, + orderPositions: make(map[string]*PositionTracker), }, nil } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices -func (e *ExchangeAssetPositionTracker) TrackNewOrder(d *Detail) error { +func (e *PositionController) TrackNewOrder(d *Detail) error { if d == nil { return ErrSubmissionIsNil } - if d.AssetType != e.Asset { + if d.AssetType != e.asset { return errAssetMismatch } - if len(e.Positions) > 0 { - for i := range e.Positions { - if e.Positions[i].Status == Open && i != len(e.Positions)-1 { - return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.Positions[i], i, len(e.Positions)-1) + if tracker, ok := e.orderPositions[d.ID]; ok { + // this has already been associated + // update the tracker + return tracker.TrackNewOrder(d) + } + if len(e.positions) > 0 { + for i := range e.positions { + if e.positions[i].status == Open && i != len(e.positions)-1 { + return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) } } - err := e.Positions[len(e.Positions)-1].TrackNewOrder(d) + err := e.positions[len(e.positions)-1].TrackNewOrder(d) if !errors.Is(err, errPositionClosed) { return err } @@ -54,18 +68,33 @@ func (e *ExchangeAssetPositionTracker) TrackNewOrder(d *Detail) error { if err != nil { return err } - e.Positions = append(e.Positions, tracker) + e.positions = append(e.positions, tracker) + + err = tracker.TrackNewOrder(d) + if err != nil { + return err + } + e.orderPositions[d.ID] = tracker + return nil +} - return tracker.TrackNewOrder(d) +func (e *PositionController) CalculateLatestPNL(pnlCalculator *PNLCalculator) (*PNLResult, error) { + if len(e.positions) == 0 { + return nil, errNoPositions + } + latest := e.positions[len(e.positions)-1] + pnlCalculator.CalculateOffline = e.offlinePNLCalculation + pnlCalculator.Amount = latest.exposure.InexactFloat64() + return latest.pnlCalculation.CalculatePNL(pnlCalculator) } // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed -func (e *ExchangeAssetPositionTracker) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code) (*PositionTracker, error) { - if e.Exchange == "" { +func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code) (*PositionTracker, error) { + if e.exchange == "" { return nil, errExchangeNameEmpty } - if e.PNLCalculation == nil { + if e.pnlCalculation == nil { return nil, errMissingPNLCalculationFunctions } if !item.IsValid() || !item.IsFutures() { @@ -76,22 +105,22 @@ func (e *ExchangeAssetPositionTracker) SetupPositionTracker(item asset.Item, pai } return &PositionTracker{ - Exchange: strings.ToLower(e.Exchange), - Asset: item, - ContractPair: pair, - UnderlyingAsset: underlying, - Status: Open, - PNLCalculation: e.PNLCalculation, - OfflinePNLCalculation: e.OfflinePNLCalculation, + exchange: strings.ToLower(e.exchange), + asset: item, + contractPair: pair, + underlyingAsset: underlying, + status: Open, + pnlCalculation: e.pnlCalculation, + offlinePNLCalculation: e.offlinePNLCalculation, }, nil } // TrackPNL calculates the PNL based on a position tracker's exposure // and current pricing. Adds the entry to PNL history to track over time func (p *PositionTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal.Decimal) error { - pnl, err := p.PNLCalculation.CalculatePNL(&PNLCalculator{ - CalculateOffline: p.OfflinePNLCalculation, - Amount: p.Exposure.InexactFloat64(), + pnl, err := p.pnlCalculation.CalculatePNL(&PNLCalculator{ + CalculateOffline: p.offlinePNLCalculation, + Amount: p.exposure.InexactFloat64(), MarkPrice: markPrice.InexactFloat64(), PrevMarkPrice: prevMarkPrice.InexactFloat64(), }) @@ -107,20 +136,20 @@ func (p *PositionTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal // TrackNewOrder knows how things are going for a given // futures contract func (p *PositionTracker) TrackNewOrder(d *Detail) error { - if p.Status == Closed { + if p.status == Closed { return errPositionClosed } if d == nil { return ErrSubmissionIsNil } - if !p.ContractPair.Equal(d.Pair) { - return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.ContractPair) + if !p.contractPair.Equal(d.Pair) { + return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.contractPair) } - if p.Exchange != strings.ToLower(d.Exchange) { - return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.Exchange) + if p.exchange != strings.ToLower(d.Exchange) { + return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.exchange) } - if p.Asset != d.AssetType { - return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.Asset) + if p.asset != d.AssetType { + return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.asset) } if d.Side == "" { return ErrSideIsInvalid @@ -128,66 +157,66 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if d.ID == "" { return ErrOrderIDNotSet } - if len(p.ShortPositions) == 0 && len(p.LongPositions) == 0 { - p.EntryPrice = decimal.NewFromFloat(d.Price) + if len(p.shortPositions) == 0 && len(p.longPositions) == 0 { + p.entryPrice = decimal.NewFromFloat(d.Price) } - for i := range p.ShortPositions { - if p.ShortPositions[i].ID == d.ID { + for i := range p.shortPositions { + if p.shortPositions[i].ID == d.ID { // update, not overwrite - ord := p.ShortPositions[i].Copy() + ord := p.shortPositions[i].Copy() ord.UpdateOrderFromDetail(d) - p.ShortPositions[i] = ord + p.shortPositions[i] = ord break } } if d.Side.IsShort() { - p.ShortPositions = append(p.ShortPositions, d.Copy()) + p.shortPositions = append(p.shortPositions, d.Copy()) } else { - p.LongPositions = append(p.LongPositions, d.Copy()) + p.longPositions = append(p.longPositions, d.Copy()) } var shortSide, longSide, averageLeverage decimal.Decimal - for i := range p.ShortPositions { - shortSide = shortSide.Add(decimal.NewFromFloat(p.ShortPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(p.ShortPositions[i].Leverage) + for i := range p.shortPositions { + shortSide = shortSide.Add(decimal.NewFromFloat(p.shortPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(p.shortPositions[i].Leverage) } - for i := range p.LongPositions { - longSide = longSide.Add(decimal.NewFromFloat(p.LongPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(p.LongPositions[i].Leverage) + for i := range p.longPositions { + longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount)) + averageLeverage = decimal.NewFromFloat(p.longPositions[i].Leverage) } - averageLeverage.Div(decimal.NewFromInt(int64(len(p.ShortPositions))).Add(decimal.NewFromInt(int64(len(p.LongPositions))))) + averageLeverage.Div(decimal.NewFromInt(int64(len(p.shortPositions))).Add(decimal.NewFromInt(int64(len(p.longPositions))))) switch { case longSide.GreaterThan(shortSide): - p.CurrentDirection = Long + p.currentDirection = Long case shortSide.GreaterThan(longSide): - p.CurrentDirection = Short + p.currentDirection = Short default: - p.CurrentDirection = UnknownSide + p.currentDirection = UnknownSide } - if p.CurrentDirection.IsLong() { - p.Exposure = longSide.Sub(shortSide) + if p.currentDirection.IsLong() { + p.exposure = longSide.Sub(shortSide) } else { - p.Exposure = shortSide.Sub(longSide) + p.exposure = shortSide.Sub(longSide) } - if p.Exposure.Equal(decimal.Zero) { + if p.exposure.Equal(decimal.Zero) { // the order is closed - p.Status = Closed - p.ClosingPrice = decimal.NewFromFloat(d.Price) - p.RealisedPNL = p.UnrealisedPNL - p.UnrealisedPNL = decimal.Zero + p.status = Closed + p.closingPrice = decimal.NewFromFloat(d.Price) + p.realisedPNL = p.unrealisedPNL + p.unrealisedPNL = decimal.Zero } - if p.Exposure.IsNegative() { + if p.exposure.IsNegative() { // tracking here has changed! - if p.CurrentDirection.IsLong() { - p.CurrentDirection = Short + if p.currentDirection.IsLong() { + p.currentDirection = Short } else { - p.CurrentDirection = Long + p.currentDirection = Long } - p.Exposure = p.Exposure.Abs() + p.exposure = p.exposure.Abs() } return nil } @@ -198,13 +227,13 @@ func (p *PositionTracker) UpsertPNLEntry(entry PNLHistory) error { if entry.Time.IsZero() { return errTimeUnset } - for i := range p.PNLHistory { - if entry.Time.Equal(p.PNLHistory[i].Time) { - p.PNLHistory[i] = entry + for i := range p.pnlHistory { + if entry.Time.Equal(p.pnlHistory[i].Time) { + p.pnlHistory[i] = entry return nil } } - p.PNLHistory = append(p.PNLHistory, entry) + p.pnlHistory = append(p.pnlHistory, entry) return nil } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 1e4c16f55d9..f9ad88a7094 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -33,9 +33,9 @@ func TestTrackPNL(t *testing.T) { fPNL := &FakePNL{ result: &PNLResult{}, } - e := ExchangeAssetPositionTracker{ - Exchange: exch, - PNLCalculation: fPNL, + e := PositionController{ + exchange: exch, + pnlCalculation: fPNL, } f, err := e.SetupPositionTracker(item, pair, pair.Base) if !errors.Is(err, nil) { @@ -64,16 +64,16 @@ func TestUpsertPNLEntry(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if len(f.PNLHistory) != 1 { - t.Errorf("expected 1 received %v", len(f.PNLHistory)) + if len(f.pnlHistory) != 1 { + t.Errorf("expected 1 received %v", len(f.pnlHistory)) } err = f.UpsertPNLEntry(PNLHistory{Time: tt}) if !errors.Is(err, nil) { t.Error(err) } - if len(f.PNLHistory) != 1 { - t.Errorf("expected 1 received %v", len(f.PNLHistory)) + if len(f.pnlHistory) != 1 { + t.Errorf("expected 1 received %v", len(f.pnlHistory)) } } @@ -85,9 +85,9 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - e := ExchangeAssetPositionTracker{ - Exchange: "test", - PNLCalculation: &FakePNL{}, + e := PositionController{ + exchange: "test", + pnlCalculation: &FakePNL{}, } f, err := e.SetupPositionTracker(item, pair, pair.Base) if !errors.Is(err, nil) { @@ -122,16 +122,16 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if !f.EntryPrice.Equal(decimal.NewFromInt(1337)) { - t.Errorf("expected 1337, received %v", f.EntryPrice) + if !f.entryPrice.Equal(decimal.NewFromInt(1337)) { + t.Errorf("expected 1337, received %v", f.entryPrice) } - if len(f.LongPositions) != 1 { + if len(f.longPositions) != 1 { t.Error("expected a long") } - if f.CurrentDirection != Long { + if f.currentDirection != Long { t.Error("expected recognition that its long") } - if f.Exposure.InexactFloat64() != od.Amount { + if f.exposure.InexactFloat64() != od.Amount { t.Error("expected 1") } @@ -142,13 +142,13 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if len(f.ShortPositions) != 1 { + if len(f.shortPositions) != 1 { t.Error("expected a short") } - if f.CurrentDirection != Long { + if f.currentDirection != Long { t.Error("expected recognition that its long") } - if f.Exposure.InexactFloat64() != 0.6 { + if f.exposure.InexactFloat64() != 0.6 { t.Error("expected 0.6") } od.Amount = 0.8 @@ -158,12 +158,11 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - - if f.CurrentDirection != Short { + if f.currentDirection != Short { t.Error("expected recognition that its short") } - if !f.Exposure.Equal(decimal.NewFromFloat(0.2)) { - t.Errorf("expected %v received %v", 0.2, f.Exposure) + if !f.exposure.Equal(decimal.NewFromFloat(0.2)) { + t.Errorf("expected %v received %v", 0.2, f.exposure) } od.ID = "5" @@ -173,10 +172,10 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if f.CurrentDirection != UnknownSide { + if f.currentDirection != UnknownSide { t.Error("expected recognition that its unknown") } - if f.Status != Closed { + if f.status != Closed { t.Error("expected closed position") } @@ -184,43 +183,43 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, errPositionClosed) { t.Error(err) } - if f.CurrentDirection != UnknownSide { + if f.currentDirection != UnknownSide { t.Error("expected recognition that its unknown") } - if f.Status != Closed { + if f.status != Closed { t.Error("expected closed position") } } func TestSetupFuturesTracker(t *testing.T) { t.Parallel() - _, err := SetupExchangeAssetPositionTracker("", "", false, nil) - if !errors.Is(err, errExchangeNameEmpty) { - t.Error(err) - } - - _, err = SetupExchangeAssetPositionTracker("test", "", false, nil) - if !errors.Is(err, errNotFutureAsset) { - t.Error(err) - } - - _, err = SetupExchangeAssetPositionTracker("test", asset.Futures, false, nil) - if !errors.Is(err, errMissingPNLCalculationFunctions) { - t.Error(err) - } - - resp, err := SetupExchangeAssetPositionTracker("test", asset.Futures, false, &FakePNL{}) - if !errors.Is(err, nil) { - t.Error(err) - } - if resp.Exchange != "test" { - t.Errorf("expected 'test' received %v", resp.Exchange) - } + //_, err := SetupPositionController("", "", false, nil) + //if !errors.Is(err, errExchangeNameEmpty) { + // t.Error(err) + //} + // + //_, err = SetupPositionController("test", "", false, nil) + //if !errors.Is(err, errNotFutureAsset) { + // t.Error(err) + //} + // + //_, err = SetupPositionController("test", asset.Futures, false, nil) + //if !errors.Is(err, errMissingPNLCalculationFunctions) { + // t.Error(err) + //} + // + //resp, err := SetupPositionController("test", asset.Futures, false, &FakePNL{}) + //if !errors.Is(err, nil) { + // t.Error(err) + //} + //if resp.exchange != "test" { + // t.Errorf("expected 'test' received %v", resp.Exchange) + //} } func TestExchangeTrackNewOrder(t *testing.T) { t.Parallel() - resp, err := SetupExchangeAssetPositionTracker("test", asset.Futures, false, &FakePNL{}) + resp, err := SetupPositionController(nil) if !errors.Is(err, nil) { t.Error(err) } @@ -235,8 +234,8 @@ func TestExchangeTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if len(resp.Positions) != 1 { - t.Errorf("expected '1' received %v", len(resp.Positions)) + if len(resp.positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.positions)) } err = resp.TrackNewOrder(&Detail{ @@ -250,8 +249,8 @@ func TestExchangeTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if len(resp.Positions) != 1 { - t.Errorf("expected '1' received %v", len(resp.Positions)) + if len(resp.positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.positions)) } err = resp.TrackNewOrder(&Detail{ @@ -265,14 +264,14 @@ func TestExchangeTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if len(resp.Positions) != 1 { - t.Errorf("expected '1' received %v", len(resp.Positions)) + if len(resp.positions) != 1 { + t.Errorf("expected '1' received %v", len(resp.positions)) } - if resp.Positions[0].Status != Closed { - t.Errorf("expected 'closed' received %v", resp.Positions[0].Status) + if resp.positions[0].status != Closed { + t.Errorf("expected 'closed' received %v", resp.positions[0].status) } - resp.Positions[0].Status = Open - resp.Positions = append(resp.Positions, resp.Positions...) + resp.positions[0].status = Open + resp.positions = append(resp.positions, resp.positions...) err = resp.TrackNewOrder(&Detail{ Exchange: "test", AssetType: asset.Futures, @@ -284,11 +283,11 @@ func TestExchangeTrackNewOrder(t *testing.T) { if !errors.Is(err, errPositionDiscrepancy) { t.Error(err) } - if len(resp.Positions) != 2 { - t.Errorf("expected '2' received %v", len(resp.Positions)) + if len(resp.positions) != 2 { + t.Errorf("expected '2' received %v", len(resp.positions)) } - resp.Positions[0].Status = Closed + resp.positions[0].status = Closed err = resp.TrackNewOrder(&Detail{ Exchange: "test", AssetType: asset.USDTMarginedFutures, diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 5e9bfef5020..1a79d0a38ad 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -18,6 +18,8 @@ var ( errPositionClosed = errors.New("the position is closed, time for a new one") errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") errAssetMismatch = errors.New("provided asset does not match") + errEmptyUnderlying = errors.New("underlying asset unset") + errNoPositions = errors.New("there are no positions") ) type PNLManagement interface { @@ -26,7 +28,7 @@ type PNLManagement interface { type CollateralManagement interface { ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) - CalculateCollateral(calculator *CollateralCalculator) (decimal.Decimal, error) + CalculateTotalCollateral([]CollateralCalculator) (decimal.Decimal, error) } type CollateralCalculator struct { @@ -34,7 +36,7 @@ type CollateralCalculator struct { Asset asset.Item Side Side CollateralAmount decimal.Decimal - EntryPrice float64 + USDPrice decimal.Decimal } type PNLCalculator struct { @@ -61,15 +63,18 @@ type PNLResult struct { IsLiquidated bool } -// ExchangeAssetPositionTracker will track the performance of +// PositionController will track the performance of // po -type ExchangeAssetPositionTracker struct { - Exchange string - Asset asset.Item - Positions []*PositionTracker - PNL decimal.Decimal - PNLCalculation PNLManagement - OfflinePNLCalculation bool +type PositionController struct { + exchange string + asset asset.Item + pair currency.Pair + underlying currency.Code + positions []*PositionTracker + orderPositions map[string]*PositionTracker + pnl decimal.Decimal + pnlCalculation PNLManagement + offlinePNLCalculation bool } // PositionTracker tracks futures orders until the overall position is considered closed @@ -80,23 +85,23 @@ type ExchangeAssetPositionTracker struct { // completely within this position tracker, however, can still provide a good // timeline of performance until the position is closed type PositionTracker struct { - Exchange string - Asset asset.Item - ContractPair currency.Pair - UnderlyingAsset currency.Code - Exposure decimal.Decimal - CurrentDirection Side - Status Status - AverageLeverage decimal.Decimal - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - ShortPositions []Detail - LongPositions []Detail - PNLHistory []PNLHistory - EntryPrice decimal.Decimal - ClosingPrice decimal.Decimal - OfflinePNLCalculation bool - PNLCalculation PNLManagement + exchange string + asset asset.Item + contractPair currency.Pair + underlyingAsset currency.Code + exposure decimal.Decimal + currentDirection Side + status Status + averageLeverage decimal.Decimal + unrealisedPNL decimal.Decimal + realisedPNL decimal.Decimal + shortPositions []Detail + longPositions []Detail + pnlHistory []PNLHistory + entryPrice decimal.Decimal + closingPrice decimal.Decimal + offlinePNLCalculation bool + pnlCalculation PNLManagement } // PNLHistory tracks how a futures contract @@ -106,3 +111,14 @@ type PNLHistory struct { UnrealisedPNL decimal.Decimal RealisedPNL decimal.Decimal } + +// PositionControllerSetup holds the parameters +// required to set up a position controller +type PositionControllerSetup struct { + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + OfflineCalculation bool + PNLCalculator PNLManagement +} From 048a6af890115253e40879b55208d67034ec5730 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 9 Dec 2021 16:43:48 +1100 Subject: [PATCH 037/171] Successfully tracks futures order positions --- exchanges/ftx/ftx_test.go | 65 +++++++++++++++++++ exchanges/order/futures.go | 45 ++++++++++++- exchanges/order/futures_test.go | 105 ++++++++++++++++++++----------- exchanges/order/futures_types.go | 1 + 4 files changed, 176 insertions(+), 40 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 5e5dda5b879..8ee0c8f4401 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -3,8 +3,10 @@ package ftx import ( "context" "errors" + "fmt" "log" "os" + "sort" "sync" "testing" "time" @@ -271,6 +273,7 @@ func TestGetPositions(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } + f.Verbose = true _, err := f.GetPositions(context.Background()) if err != nil { t.Error(err) @@ -1720,3 +1723,65 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { err) } } + +func TestRealsies(t *testing.T) { + f.Verbose = true + result, err := f.FetchOrderHistory(context.Background(), "BTC-1231", time.Time{}, time.Time{}, "100") + if err != nil { + t.Error(err) + } + sort.Slice(result, func(i, j int) bool { + return result[i].CreatedAt.Before(result[j].CreatedAt) + }) + + pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) + var orders []order.Detail + for i := range result { + price := result[i].Price + if price == 0 { + price = result[i].AvgFillPrice + } + side, err := order.StringToOrderSide(result[i].Side) + if err != nil { + t.Error(err) + } + orders = append(orders, order.Detail{ + Side: side, + Pair: pair, + ID: fmt.Sprintf("%v", result[i].ID), + Price: price, + Amount: result[i].Size, + Status: order.Status(result[i].Status), + AssetType: asset.Futures, + Exchange: f.Name, + Date: result[i].CreatedAt, + }) + } + + exch := f.Name + item := asset.Futures + setup := &order.PositionControllerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + PNLCalculator: &f, + } + p, err := order.SetupPositionController(setup) + if err != nil { + t.Error(err) + } + for i := range orders { + err = p.TrackNewOrder(&orders[i]) + if err != nil { + t.Error(err) + } + } + pos := p.GetPositions() + for i := range pos { + pnl, _ := pos[i].CalculatePNL() + t.Logf("%+v", pnl.String()) + + } + +} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index e3eba330cf8..db9d4adeffc 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -13,6 +13,9 @@ import ( // SetupPositionController creates a futures order tracker for a specific exchange func SetupPositionController(setup *PositionControllerSetup) (*PositionController, error) { + if setup == nil { + return nil, errNilSetup + } if setup.Exchange == "" { return nil, errExchangeNameEmpty } @@ -39,6 +42,10 @@ func SetupPositionController(setup *PositionControllerSetup) (*PositionControlle }, nil } +func (e *PositionController) GetPositions() []*PositionTracker { + return e.positions +} + // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices func (e *PositionController) TrackNewOrder(d *Detail) error { @@ -59,9 +66,13 @@ func (e *PositionController) TrackNewOrder(d *Detail) error { return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) } } - err := e.positions[len(e.positions)-1].TrackNewOrder(d) - if !errors.Is(err, errPositionClosed) { - return err + if e.positions[len(e.positions)-1].status == Open { + err := e.positions[len(e.positions)-1].TrackNewOrder(d) + if err != nil && !errors.Is(err, errPositionClosed) { + return err + } + e.orderPositions[d.ID] = e.positions[len(e.positions)-1] + return nil } } tracker, err := e.SetupPositionTracker(d.AssetType, d.Pair, d.Pair.Base) @@ -133,6 +144,21 @@ func (p *PositionTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal }) } +func (p *PositionTracker) CalculateClosedPositionPNL() (decimal.Decimal, error) { + if p.status != Closed { + return decimal.Zero, errors.New("not closed") + } + + var shortStanding, longStanding decimal.Decimal + for i := range p.shortPositions { + shortStanding = shortStanding.Add(decimal.NewFromFloat(p.shortPositions[i].Amount).Mul(decimal.NewFromFloat(p.shortPositions[i].Price))) + } + for i := range p.longPositions { + longStanding = longStanding.Add(decimal.NewFromFloat(p.longPositions[i].Amount).Mul(decimal.NewFromFloat(p.longPositions[i].Price))) + } + return longStanding.Sub(shortStanding).Abs(), nil +} + // TrackNewOrder knows how things are going for a given // futures contract func (p *PositionTracker) TrackNewOrder(d *Detail) error { @@ -170,6 +196,14 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { break } } + for i := range p.longPositions { + if p.longPositions[i].ID == d.ID { + ord := p.longPositions[i].Copy() + ord.UpdateOrderFromDetail(d) + p.longPositions[i] = ord + break + } + } if d.Side.IsShort() { p.shortPositions = append(p.shortPositions, d.Copy()) @@ -208,6 +242,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.closingPrice = decimal.NewFromFloat(d.Price) p.realisedPNL = p.unrealisedPNL p.unrealisedPNL = decimal.Zero + return nil } if p.exposure.IsNegative() { // tracking here has changed! @@ -221,6 +256,10 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return nil } +func (p *PositionTracker) TrackPrice(price decimal.Decimal) decimal.Decimal { + return p.exposure.Mul(price) +} + // UpsertPNLEntry upserts an entry to PNLHistory field // with some basic checks func (p *PositionTracker) UpsertPNLEntry(entry PNLHistory) error { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index f9ad88a7094..9d52cf6cae7 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -193,40 +193,71 @@ func TestTrackNewOrder(t *testing.T) { func TestSetupFuturesTracker(t *testing.T) { t.Parallel() - //_, err := SetupPositionController("", "", false, nil) - //if !errors.Is(err, errExchangeNameEmpty) { - // t.Error(err) - //} - // - //_, err = SetupPositionController("test", "", false, nil) - //if !errors.Is(err, errNotFutureAsset) { - // t.Error(err) - //} - // - //_, err = SetupPositionController("test", asset.Futures, false, nil) - //if !errors.Is(err, errMissingPNLCalculationFunctions) { - // t.Error(err) - //} - // - //resp, err := SetupPositionController("test", asset.Futures, false, &FakePNL{}) - //if !errors.Is(err, nil) { - // t.Error(err) - //} - //if resp.exchange != "test" { - // t.Errorf("expected 'test' received %v", resp.Exchange) - //} + + _, err := SetupPositionController(nil) + if !errors.Is(err, errNilSetup) { + t.Error(err) + } + + setup := &PositionControllerSetup{} + _, err = SetupPositionController(setup) + if !errors.Is(err, errExchangeNameEmpty) { + t.Error(err) + } + setup.Exchange = "test" + _, err = SetupPositionController(setup) + if !errors.Is(err, errNotFutureAsset) { + t.Error(err) + } + setup.Asset = asset.Futures + _, err = SetupPositionController(setup) + if !errors.Is(err, ErrPairIsEmpty) { + t.Error(err) + } + + setup.Pair = currency.NewPair(currency.BTC, currency.USDT) + _, err = SetupPositionController(setup) + if !errors.Is(err, errEmptyUnderlying) { + t.Error(err) + } + + setup.Underlying = currency.BTC + _, err = SetupPositionController(setup) + if !errors.Is(err, errMissingPNLCalculationFunctions) { + t.Error(err) + } + + setup.PNLCalculator = &FakePNL{} + resp, err := SetupPositionController(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + if resp.exchange != "test" { + t.Errorf("expected 'test' received %v", resp.exchange) + } } func TestExchangeTrackNewOrder(t *testing.T) { t.Parallel() - resp, err := SetupPositionController(nil) + exch := "test" + item := asset.Futures + pair := currency.NewPair(currency.BTC, currency.USDT) + setup := &PositionControllerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + PNLCalculator: &FakePNL{}, + } + resp, err := SetupPositionController(setup) if !errors.Is(err, nil) { t.Error(err) } + err = resp.TrackNewOrder(&Detail{ - Exchange: "test", - AssetType: asset.Futures, - Pair: currency.NewPair(currency.BTC, currency.USDT), + Exchange: exch, + AssetType: item, + Pair: pair, Side: Short, ID: "1", Amount: 1, @@ -239,9 +270,9 @@ func TestExchangeTrackNewOrder(t *testing.T) { } err = resp.TrackNewOrder(&Detail{ - Exchange: "test", - AssetType: asset.Futures, - Pair: currency.NewPair(currency.BTC, currency.USDT), + Exchange: exch, + AssetType: item, + Pair: pair, Side: Short, ID: "1", Amount: 1, @@ -254,9 +285,9 @@ func TestExchangeTrackNewOrder(t *testing.T) { } err = resp.TrackNewOrder(&Detail{ - Exchange: "test", - AssetType: asset.Futures, - Pair: currency.NewPair(currency.BTC, currency.USDT), + Exchange: exch, + AssetType: item, + Pair: pair, Side: Long, ID: "2", Amount: 2, @@ -273,9 +304,9 @@ func TestExchangeTrackNewOrder(t *testing.T) { resp.positions[0].status = Open resp.positions = append(resp.positions, resp.positions...) err = resp.TrackNewOrder(&Detail{ - Exchange: "test", - AssetType: asset.Futures, - Pair: currency.NewPair(currency.BTC, currency.USDT), + Exchange: exch, + AssetType: item, + Pair: pair, Side: Long, ID: "2", Amount: 2, @@ -289,9 +320,9 @@ func TestExchangeTrackNewOrder(t *testing.T) { resp.positions[0].status = Closed err = resp.TrackNewOrder(&Detail{ - Exchange: "test", + Exchange: exch, + Pair: pair, AssetType: asset.USDTMarginedFutures, - Pair: currency.NewPair(currency.BTC, currency.USDT), Side: Long, ID: "2", Amount: 2, diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 1a79d0a38ad..0749c29662f 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -20,6 +20,7 @@ var ( errAssetMismatch = errors.New("provided asset does not match") errEmptyUnderlying = errors.New("underlying asset unset") errNoPositions = errors.New("there are no positions") + errNilSetup = errors.New("nil setup received") ) type PNLManagement interface { From f06542acf927644bf9f4c35285758fd3b42fb919 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 13 Dec 2021 16:39:50 +1100 Subject: [PATCH 038/171] Fails to calculate PNL --- exchanges/ftx/ftx_test.go | 7 +- exchanges/ftx/ftx_wrapper.go | 11 +-- exchanges/order/futures.go | 114 ++++++++++++++++++------------- exchanges/order/futures_test.go | 4 +- exchanges/order/futures_types.go | 36 +++++----- 5 files changed, 99 insertions(+), 73 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 8ee0c8f4401..9146e59edb2 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1779,8 +1779,11 @@ func TestRealsies(t *testing.T) { } pos := p.GetPositions() for i := range pos { - pnl, _ := pos[i].CalculatePNL() - t.Logf("%+v", pnl.String()) + lol, err := pos[i].GetRealisedPNL() + if err != nil { + t.Error(err) + } + t.Logf("%v", lol) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index e3181688828..6e176e74937 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1279,8 +1279,11 @@ func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice floa func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { var result order.PNLResult - if pnl.CalculateOffline { - uPNL := f.CalculateUnrealisedPNL(pnl.Amount, pnl.MarkPrice, pnl.PrevMarkPrice) + if pnl.ExchangeBasedCalculation == nil { + return nil, order.ErrAmountExceedsMax + } + if pnl.ExchangeBasedCalculation.CalculateOffline { + uPNL := f.CalculateUnrealisedPNL(pnl.ExchangeBasedCalculation.Amount, pnl.ExchangeBasedCalculation.CurrentPrice, pnl.ExchangeBasedCalculation.PreviousPrice) result.UnrealisedPNL = decimal.NewFromFloat(uPNL) return &result, nil } else { @@ -1290,7 +1293,7 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { return nil, err } if info.Liquidating || info.Collateral == 0 { - result.IsLiquidated = true + // result.IsLiquidated = true return &result, nil } for i := range info.Positions { @@ -1304,7 +1307,7 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { default: return nil, order.ErrSideIsInvalid } - if info.Positions[i].EntryPrice == pnl.EntryPrice && pnl.Side == pnlSide { + if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice && pnl.ExchangeBasedCalculation.Side == pnlSide { result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPnL) result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPnL) return &result, nil diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index db9d4adeffc..f12295e5ac0 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -9,6 +9,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a futures order tracker for a specific exchange @@ -89,25 +90,12 @@ func (e *PositionController) TrackNewOrder(d *Detail) error { return nil } -func (e *PositionController) CalculateLatestPNL(pnlCalculator *PNLCalculator) (*PNLResult, error) { - if len(e.positions) == 0 { - return nil, errNoPositions - } - latest := e.positions[len(e.positions)-1] - pnlCalculator.CalculateOffline = e.offlinePNLCalculation - pnlCalculator.Amount = latest.exposure.InexactFloat64() - return latest.pnlCalculation.CalculatePNL(pnlCalculator) -} - // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code) (*PositionTracker, error) { if e.exchange == "" { return nil, errExchangeNameEmpty } - if e.pnlCalculation == nil { - return nil, errMissingPNLCalculationFunctions - } if !item.IsValid() || !item.IsFutures() { return nil, errNotFutureAsset } @@ -115,48 +103,76 @@ func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency return nil, ErrPairIsEmpty } - return &PositionTracker{ - exchange: strings.ToLower(e.exchange), - asset: item, - contractPair: pair, - underlyingAsset: underlying, - status: Open, - pnlCalculation: e.pnlCalculation, - offlinePNLCalculation: e.offlinePNLCalculation, - }, nil + resp := &PositionTracker{ + exchange: strings.ToLower(e.exchange), + asset: item, + contractPair: pair, + underlyingAsset: underlying, + status: Open, + pnlCalculation: e.pnlCalculation, + } + if e.pnlCalculation == nil { + log.Warnf(log.OrderMgr, "no pnl calculation functions supplied for %v %v %v, using default calculations", e.exchange, e.asset, e.pair) + e.pnlCalculation = resp + } + return resp, nil +} + +// CalculatePNL this is a localised generic way of calculating open +// positions' worth +func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { + result := &PNLResult{} + var amount, price decimal.Decimal + if calc.OrderBasedCalculation != nil { + result.Time = calc.OrderBasedCalculation.Date + amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) + price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) + if (p.currentDirection == Long && calc.OrderBasedCalculation.Side.IsShort()) || + (p.currentDirection == Short && calc.OrderBasedCalculation.Side.IsLong()) { + currPos := p.exposure.Mul(price) + result.UnrealisedPNL = currPos.Sub(price.Mul(amount)) + + } else if (p.currentDirection == Long && calc.OrderBasedCalculation.Side.IsLong()) || + (p.currentDirection == Short && calc.OrderBasedCalculation.Side.IsShort()) { + currPos := p.exposure.Mul(price) + result.UnrealisedPNL = currPos.Add(price.Mul(amount)) + } + return result, nil + } else if calc.TimeBasedCalculation != nil { + price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) + result.UnrealisedPNL = p.exposure.Mul(price) + return result, nil + } + + return nil, errMissingPNLCalculationFunctions } -// TrackPNL calculates the PNL based on a position tracker's exposure +// TrackPNLByTime calculates the PNL based on a position tracker's exposure // and current pricing. Adds the entry to PNL history to track over time -func (p *PositionTracker) TrackPNL(t time.Time, markPrice, prevMarkPrice decimal.Decimal) error { +func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) error { + defer func() { + p.latestPrice = decimal.NewFromFloat(currentPrice) + }() pnl, err := p.pnlCalculation.CalculatePNL(&PNLCalculator{ - CalculateOffline: p.offlinePNLCalculation, - Amount: p.exposure.InexactFloat64(), - MarkPrice: markPrice.InexactFloat64(), - PrevMarkPrice: prevMarkPrice.InexactFloat64(), + TimeBasedCalculation: &TimeBasedCalculation{ + currentPrice, + }, }) if err != nil { return err } - return p.UpsertPNLEntry(PNLHistory{ + return p.UpsertPNLEntry(PNLResult{ Time: t, UnrealisedPNL: pnl.UnrealisedPNL, + RealisedPNL: pnl.RealisedPNL, }) } -func (p *PositionTracker) CalculateClosedPositionPNL() (decimal.Decimal, error) { +func (p *PositionTracker) GetRealisedPNL() (decimal.Decimal, error) { if p.status != Closed { - return decimal.Zero, errors.New("not closed") + return decimal.Zero, errors.New("position not closed") } - - var shortStanding, longStanding decimal.Decimal - for i := range p.shortPositions { - shortStanding = shortStanding.Add(decimal.NewFromFloat(p.shortPositions[i].Amount).Mul(decimal.NewFromFloat(p.shortPositions[i].Price))) - } - for i := range p.longPositions { - longStanding = longStanding.Add(decimal.NewFromFloat(p.longPositions[i].Amount).Mul(decimal.NewFromFloat(p.longPositions[i].Price))) - } - return longStanding.Sub(shortStanding).Abs(), nil + return p.realisedPNL, nil } // TrackNewOrder knows how things are going for a given @@ -231,21 +247,27 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { default: p.currentDirection = UnknownSide } + + result, err := p.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) + if err != nil { + return err + } + err = p.UpsertPNLEntry(*result) + if err != nil { + return err + } + if p.currentDirection.IsLong() { p.exposure = longSide.Sub(shortSide) } else { p.exposure = shortSide.Sub(longSide) } if p.exposure.Equal(decimal.Zero) { - // the order is closed p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) p.realisedPNL = p.unrealisedPNL p.unrealisedPNL = decimal.Zero - return nil - } - if p.exposure.IsNegative() { - // tracking here has changed! + } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short } else { @@ -262,7 +284,7 @@ func (p *PositionTracker) TrackPrice(price decimal.Decimal) decimal.Decimal { // UpsertPNLEntry upserts an entry to PNLHistory field // with some basic checks -func (p *PositionTracker) UpsertPNLEntry(entry PNLHistory) error { +func (p *PositionTracker) UpsertPNLEntry(entry PNLResult) error { if entry.Time.IsZero() { return errTimeUnset } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 9d52cf6cae7..8f99519da87 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -41,12 +41,12 @@ func TestTrackPNL(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - err = f.TrackPNL(time.Now(), decimal.Zero, decimal.Zero) + err = f.TrackPNLByTime(time.Now(), decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Error(err) } fPNL.err = errMissingPNLCalculationFunctions - err = f.TrackPNL(time.Now(), decimal.Zero, decimal.Zero) + err = f.TrackPNLByTime(time.Now(), decimal.Zero, decimal.Zero) if !errors.Is(err, errMissingPNLCalculationFunctions) { t.Error(err) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 0749c29662f..00256c0011d 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -41,27 +41,32 @@ type CollateralCalculator struct { } type PNLCalculator struct { + OrderBasedCalculation *Detail + TimeBasedCalculation *TimeBasedCalculation + ExchangeBasedCalculation *ExchangeBasedCalculation +} + +type TimeBasedCalculation struct { + CurrentPrice float64 +} + +type ExchangeBasedCalculation struct { CalculateOffline bool Underlying currency.Code - OrderID string Asset asset.Item Side Side Leverage float64 EntryPrice float64 - OpeningAmount float64 + EntryAmount float64 Amount float64 - MarkPrice float64 - PrevMarkPrice float64 CurrentPrice float64 + PreviousPrice float64 } type PNLResult struct { - MarginFraction decimal.Decimal - EstimatedLiquidationPrice decimal.Decimal - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - Collateral decimal.Decimal - IsLiquidated bool + Time time.Time + UnrealisedPNL decimal.Decimal + RealisedPNL decimal.Decimal } // PositionController will track the performance of @@ -98,19 +103,12 @@ type PositionTracker struct { realisedPNL decimal.Decimal shortPositions []Detail longPositions []Detail - pnlHistory []PNLHistory + pnlHistory []PNLResult entryPrice decimal.Decimal closingPrice decimal.Decimal offlinePNLCalculation bool pnlCalculation PNLManagement -} - -// PNLHistory tracks how a futures contract -// pnl is going over the history of exposure -type PNLHistory struct { - Time time.Time - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal + latestPrice decimal.Decimal } // PositionControllerSetup holds the parameters From 6e06e26967b2230f609528d27b1ae601e95c427f Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 14 Dec 2021 17:07:51 +1100 Subject: [PATCH 039/171] Calculates pnl from orders accurately with exception to flipping orders --- exchanges/ftx/ftx_test.go | 11 ++- exchanges/order/futures.go | 133 ++++++++++++++++++++++++------------- 2 files changed, 96 insertions(+), 48 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 9146e59edb2..3119211481d 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -2,6 +2,7 @@ package ftx import ( "context" + "encoding/json" "errors" "fmt" "log" @@ -1725,11 +1726,15 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { } func TestRealsies(t *testing.T) { - f.Verbose = true - result, err := f.FetchOrderHistory(context.Background(), "BTC-1231", time.Time{}, time.Time{}, "100") + pressXToJSON := `{"success":true,"result":[{"id":102360167561,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49318.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:31:09.246156+00:00","future":"BTC-1231"},{"id":102359914432,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49311.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:30:02.546539+00:00","future":"BTC-1231"},{"id":102359875376,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49289.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:29:48.461182+00:00","future":"BTC-1231"},{"id":102357575195,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49205.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:17:35.686416+00:00","future":"BTC-1231"},{"id":102356768854,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49248.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:13:28.891973+00:00","future":"BTC-1231"},{"id":102334270248,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49068.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:41.551614+00:00","future":"BTC-1231"},{"id":102334231437,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0004,"status":"closed","filledSize":0.0004,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49050.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:27.615861+00:00","future":"BTC-1231"},{"id":102333965786,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0003,"status":"closed","filledSize":0.0003,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49030.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:19:04.483189+00:00","future":"BTC-1231"},{"id":102333762284,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49061.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:07.703362+00:00","future":"BTC-1231"},{"id":102333732631,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49063.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:02.044371+00:00","future":"BTC-1231"},{"id":102321616290,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":48637.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:35.419340+00:00","future":"BTC-1231"},{"id":102321498868,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48624.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:07.428170+00:00","future":"BTC-1231"},{"id":102321095447,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48633.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:26:34.077562+00:00","future":"BTC-1231"},{"id":102320767608,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48720.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:25:32.920109+00:00","future":"BTC-1231"},{"id":101062637645,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":57487.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:45:32.636504+00:00","future":"BTC-1231"},{"id":101062454431,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:44.588077+00:00","future":"BTC-1231"},{"id":101062444084,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:40.982596+00:00","future":"BTC-1231"},{"id":92221164777,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":61679.0,"postOnly":false,"ioc":true,"createdAt":"2021-11-01T02:25:43.913874+00:00","future":"BTC-1231"},{"id":92186217479,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":62902.0,"postOnly":false,"ioc":true,"createdAt":"2021-10-31T23:38:26.914224+00:00","future":"BTC-1231"}],"hasMoreData":false}` + resp := struct { + Data []OrderData `json:"result"` + }{} + err := json.Unmarshal([]byte(pressXToJSON), &resp) if err != nil { - t.Error(err) + t.Fatal() } + result := resp.Data sort.Slice(result, func(i, j int) bool { return result[i].CreatedAt.Before(result[j].CreatedAt) }) diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index f12295e5ac0..fc24f598515 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -76,7 +76,7 @@ func (e *PositionController) TrackNewOrder(d *Detail) error { return nil } } - tracker, err := e.SetupPositionTracker(d.AssetType, d.Pair, d.Pair.Base) + tracker, err := e.SetupPositionTracker(d.AssetType, d.Pair, d.Pair.Base, d.Price, d.Side) if err != nil { return err } @@ -92,7 +92,7 @@ func (e *PositionController) TrackNewOrder(d *Detail) error { // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed -func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code) (*PositionTracker, error) { +func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code, entryPrice float64, direction Side) (*PositionTracker, error) { if e.exchange == "" { return nil, errExchangeNameEmpty } @@ -104,12 +104,14 @@ func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency } resp := &PositionTracker{ - exchange: strings.ToLower(e.exchange), - asset: item, - contractPair: pair, - underlyingAsset: underlying, - status: Open, - pnlCalculation: e.pnlCalculation, + exchange: strings.ToLower(e.exchange), + asset: item, + contractPair: pair, + underlyingAsset: underlying, + status: Open, + pnlCalculation: e.pnlCalculation, + entryPrice: decimal.NewFromFloat(entryPrice), + currentDirection: direction, } if e.pnlCalculation == nil { log.Warnf(log.OrderMgr, "no pnl calculation functions supplied for %v %v %v, using default calculations", e.exchange, e.asset, e.pair) @@ -118,35 +120,6 @@ func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency return resp, nil } -// CalculatePNL this is a localised generic way of calculating open -// positions' worth -func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { - result := &PNLResult{} - var amount, price decimal.Decimal - if calc.OrderBasedCalculation != nil { - result.Time = calc.OrderBasedCalculation.Date - amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) - price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) - if (p.currentDirection == Long && calc.OrderBasedCalculation.Side.IsShort()) || - (p.currentDirection == Short && calc.OrderBasedCalculation.Side.IsLong()) { - currPos := p.exposure.Mul(price) - result.UnrealisedPNL = currPos.Sub(price.Mul(amount)) - - } else if (p.currentDirection == Long && calc.OrderBasedCalculation.Side.IsLong()) || - (p.currentDirection == Short && calc.OrderBasedCalculation.Side.IsShort()) { - currPos := p.exposure.Mul(price) - result.UnrealisedPNL = currPos.Add(price.Mul(amount)) - } - return result, nil - } else if calc.TimeBasedCalculation != nil { - price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) - result.UnrealisedPNL = p.exposure.Mul(price) - return result, nil - } - - return nil, errMissingPNLCalculationFunctions -} - // TrackPNLByTime calculates the PNL based on a position tracker's exposure // and current pricing. Adds the entry to PNL history to track over time func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) error { @@ -238,14 +211,8 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } averageLeverage.Div(decimal.NewFromInt(int64(len(p.shortPositions))).Add(decimal.NewFromInt(int64(len(p.longPositions))))) - - switch { - case longSide.GreaterThan(shortSide): - p.currentDirection = Long - case shortSide.GreaterThan(longSide): - p.currentDirection = Short - default: - p.currentDirection = UnknownSide + if p.currentDirection == "" { + p.currentDirection = d.Side } result, err := p.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) @@ -256,7 +223,13 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if err != nil { return err } + p.unrealisedPNL = result.UnrealisedPNL + if longSide.GreaterThan(shortSide) { + p.currentDirection = Long + } else if shortSide.GreaterThan(longSide) { + p.currentDirection = Short + } if p.currentDirection.IsLong() { p.exposure = longSide.Sub(shortSide) } else { @@ -278,6 +251,76 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return nil } +// CalculatePNL this is a localised generic way of calculating open +// positions' worth +func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { + result := &PNLResult{} + var price, amount decimal.Decimal + var err error + if calc.OrderBasedCalculation != nil { + result.Time = calc.OrderBasedCalculation.Date + price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) + amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) + + if (p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && + p.exposure.LessThan(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)) { + // we need to handle the pnl twice as we're flipping direction + result2 := &PNLResult{} + + one := p.exposure.Sub(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)).Abs() + result, err = p.idkYet(calc, result, one, price) + if err != nil { + return nil, err + } + p.unrealisedPNL = result.UnrealisedPNL + if calc.OrderBasedCalculation.Side.IsShort() { + calc.OrderBasedCalculation.Side = Long + } else if calc.OrderBasedCalculation.Side.IsLong() { + calc.OrderBasedCalculation.Side = Short + } + two := decimal.NewFromFloat(calc.OrderBasedCalculation.Amount).Sub(p.exposure).Abs() + result2, err = p.idkYet(calc, result2, two, price) + if err != nil { + return nil, err + } + result.UnrealisedPNL = result.UnrealisedPNL.Add(result2.UnrealisedPNL) + p.unrealisedPNL = result.UnrealisedPNL + if calc.OrderBasedCalculation.Side.IsShort() { + calc.OrderBasedCalculation.Side = Long + } else if calc.OrderBasedCalculation.Side.IsLong() { + calc.OrderBasedCalculation.Side = Short + } + } + + result, err = p.idkYet(calc, result, amount, price) + if err != nil { + return nil, err + } + return result, nil + } else if calc.TimeBasedCalculation != nil { + price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) + diff := p.entryPrice.Sub(price) + result.UnrealisedPNL = p.exposure.Mul(diff) + return result, nil + } + + return nil, errMissingPNLCalculationFunctions +} + +func (p *PositionTracker) idkYet(calc *PNLCalculator, result *PNLResult, amount decimal.Decimal, price decimal.Decimal) (*PNLResult, error) { + switch { + case p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsShort(), + p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsLong(): + result.UnrealisedPNL = p.unrealisedPNL.Add(amount.Mul(price)) + case p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong(), + p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort(): + result.UnrealisedPNL = p.unrealisedPNL.Sub(amount.Mul(price)) + default: + return nil, fmt.Errorf("%v %v %v %v whats wrong", p.currentDirection, calc.OrderBasedCalculation.Side, result.UnrealisedPNL, amount.Mul(price)) + } + return result, nil +} + func (p *PositionTracker) TrackPrice(price decimal.Decimal) decimal.Decimal { return p.exposure.Mul(price) } From ec18f8377c89867e59adeb1b5309c28ca8a067ed Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 15 Dec 2021 11:53:08 +1100 Subject: [PATCH 040/171] Calculates PNL from orders --- exchanges/ftx/ftx_test.go | 58 +++++++++++++++++++++++++++++--------- exchanges/order/futures.go | 17 ++++++----- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 3119211481d..3261e45062e 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" @@ -1725,8 +1726,28 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { } } -func TestRealsies(t *testing.T) { - pressXToJSON := `{"success":true,"result":[{"id":102360167561,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49318.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:31:09.246156+00:00","future":"BTC-1231"},{"id":102359914432,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49311.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:30:02.546539+00:00","future":"BTC-1231"},{"id":102359875376,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49289.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:29:48.461182+00:00","future":"BTC-1231"},{"id":102357575195,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49205.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:17:35.686416+00:00","future":"BTC-1231"},{"id":102356768854,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49248.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:13:28.891973+00:00","future":"BTC-1231"},{"id":102334270248,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49068.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:41.551614+00:00","future":"BTC-1231"},{"id":102334231437,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0004,"status":"closed","filledSize":0.0004,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49050.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:27.615861+00:00","future":"BTC-1231"},{"id":102333965786,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0003,"status":"closed","filledSize":0.0003,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49030.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:19:04.483189+00:00","future":"BTC-1231"},{"id":102333762284,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49061.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:07.703362+00:00","future":"BTC-1231"},{"id":102333732631,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49063.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:02.044371+00:00","future":"BTC-1231"},{"id":102321616290,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":48637.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:35.419340+00:00","future":"BTC-1231"},{"id":102321498868,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48624.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:07.428170+00:00","future":"BTC-1231"},{"id":102321095447,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48633.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:26:34.077562+00:00","future":"BTC-1231"},{"id":102320767608,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48720.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:25:32.920109+00:00","future":"BTC-1231"},{"id":101062637645,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":57487.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:45:32.636504+00:00","future":"BTC-1231"},{"id":101062454431,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:44.588077+00:00","future":"BTC-1231"},{"id":101062444084,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:40.982596+00:00","future":"BTC-1231"},{"id":92221164777,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":61679.0,"postOnly":false,"ioc":true,"createdAt":"2021-11-01T02:25:43.913874+00:00","future":"BTC-1231"},{"id":92186217479,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":62902.0,"postOnly":false,"ioc":true,"createdAt":"2021-10-31T23:38:26.914224+00:00","future":"BTC-1231"}],"hasMoreData":false}` +func TestCalculatePNLFromOrders(t *testing.T) { + pressXToJSON := `{"success":true,"result":[ + {"id":102360167561,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49318.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:31:09.246156+00:00","future":"BTC-1231"}, + {"id":102359914432,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49311.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:30:02.546539+00:00","future":"BTC-1231"}, + {"id":102359875376,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49289.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:29:48.461182+00:00","future":"BTC-1231"}, + {"id":102357575195,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49205.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:17:35.686416+00:00","future":"BTC-1231"}, + {"id":102356768854,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49248.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:13:28.891973+00:00","future":"BTC-1231"}, + {"id":102334270248,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49068.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:41.551614+00:00","future":"BTC-1231"}, + {"id":102334231437,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0004,"status":"closed","filledSize":0.0004,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49050.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:27.615861+00:00","future":"BTC-1231"}, + {"id":102333965786,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0003,"status":"closed","filledSize":0.0003,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49030.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:19:04.483189+00:00","future":"BTC-1231"}, + {"id":102333762284,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49061.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:07.703362+00:00","future":"BTC-1231"}, + {"id":102333732631,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49063.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:02.044371+00:00","future":"BTC-1231"}, + {"id":102321616290,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":48637.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:35.419340+00:00","future":"BTC-1231"}, + {"id":102321498868,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48624.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:07.428170+00:00","future":"BTC-1231"}, + {"id":102321095447,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48633.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:26:34.077562+00:00","future":"BTC-1231"}, + {"id":102320767608,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48720.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:25:32.920109+00:00","future":"BTC-1231"}, + {"id":101062637645,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":57487.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:45:32.636504+00:00","future":"BTC-1231"}, + {"id":101062454431,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:44.588077+00:00","future":"BTC-1231"}, + {"id":101062444084,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:40.982596+00:00","future":"BTC-1231"}, + {"id":92221164777,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":61679.0,"postOnly":false,"ioc":true,"createdAt":"2021-11-01T02:25:43.913874+00:00","future":"BTC-1231"}, + {"id":92186217479,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":62902.0,"postOnly":false,"ioc":true,"createdAt":"2021-10-31T23:38:26.914224+00:00","future":"BTC-1231"} +],"hasMoreData":false}` resp := struct { Data []OrderData `json:"result"` }{} @@ -1742,10 +1763,7 @@ func TestRealsies(t *testing.T) { pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) var orders []order.Detail for i := range result { - price := result[i].Price - if price == 0 { - price = result[i].AvgFillPrice - } + price := result[i].AvgFillPrice side, err := order.StringToOrderSide(result[i].Side) if err != nil { t.Error(err) @@ -1783,13 +1801,25 @@ func TestRealsies(t *testing.T) { } } pos := p.GetPositions() - for i := range pos { - lol, err := pos[i].GetRealisedPNL() - if err != nil { - t.Error(err) - } - t.Logf("%v", lol) - + if len(pos) != 6 { + t.Fatal("expected 6 positions") + } + if pnl, err := pos[0].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.1223)) || err != nil { + t.Errorf("expected nil err, received '%v', expected 0.1223, received '%v'", err, pnl) + } + if pnl, err := pos[1].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0148)) || err != nil { + t.Errorf("expected nil err, received '%v', expected 0.0148, received '%v'", err, pnl) + } + if pnl, err := pos[2].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0092)) || err != nil { + t.Errorf("expected nil err, received '%v', expected 0.0092, received '%v'", err, pnl) + } + if pnl, err := pos[3].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0054)) || err != nil { + t.Errorf("expected nil err, received '%v', expected -0.0054, received '%v'", err, pnl) + } + if pnl, err := pos[4].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0387)) || err != nil { + t.Errorf("expected nil err, received '%v', expected 0.0387, received '%v'", err, pnl) + } + if pnl, err := pos[5].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0029)) || err != nil { + t.Errorf("expected nil err, received '%v', expected -0.0029, received '%v'", err, pnl) } - } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index fc24f598515..b62f34732c8 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -265,9 +265,11 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) if (p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && p.exposure.LessThan(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)) { // we need to handle the pnl twice as we're flipping direction - result2 := &PNLResult{} + result2 := &PNLResult{ + Time: result.Time, + } - one := p.exposure.Sub(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)).Abs() + one := p.exposure.Sub(amount).Abs() result, err = p.idkYet(calc, result, one, price) if err != nil { return nil, err @@ -278,18 +280,18 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) } else if calc.OrderBasedCalculation.Side.IsLong() { calc.OrderBasedCalculation.Side = Short } - two := decimal.NewFromFloat(calc.OrderBasedCalculation.Amount).Sub(p.exposure).Abs() + two := amount.Sub(p.exposure).Abs() result2, err = p.idkYet(calc, result2, two, price) if err != nil { return nil, err } - result.UnrealisedPNL = result.UnrealisedPNL.Add(result2.UnrealisedPNL) - p.unrealisedPNL = result.UnrealisedPNL + p.unrealisedPNL = result2.UnrealisedPNL if calc.OrderBasedCalculation.Side.IsShort() { calc.OrderBasedCalculation.Side = Long } else if calc.OrderBasedCalculation.Side.IsLong() { calc.OrderBasedCalculation.Side = Short } + return result2, nil } result, err = p.idkYet(calc, result, amount, price) @@ -308,13 +310,14 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) } func (p *PositionTracker) idkYet(calc *PNLCalculator, result *PNLResult, amount decimal.Decimal, price decimal.Decimal) (*PNLResult, error) { + hello := amount.Mul(price) switch { case p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsShort(), p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsLong(): - result.UnrealisedPNL = p.unrealisedPNL.Add(amount.Mul(price)) + result.UnrealisedPNL = p.unrealisedPNL.Add(hello) case p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong(), p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort(): - result.UnrealisedPNL = p.unrealisedPNL.Sub(amount.Mul(price)) + result.UnrealisedPNL = p.unrealisedPNL.Sub(hello) default: return nil, fmt.Errorf("%v %v %v %v whats wrong", p.currentDirection, calc.OrderBasedCalculation.Side, result.UnrealisedPNL, amount.Mul(price)) } From 8bdd4a2104573de12e126db6383d600d950ced3c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 17 Dec 2021 13:59:22 +1100 Subject: [PATCH 041/171] Adds another controller layer to make it ez from orderstore --- .../portfolio/compliance/compliance_types.go | 8 +- .../eventhandlers/portfolio/portfolio.go | 14 +- engine/order_manager.go | 36 +++- engine/order_manager_types.go | 12 +- exchanges/ftx/ftx_test.go | 15 +- exchanges/ftx/ftx_wrapper.go | 50 +++-- exchanges/order/futures.go | 184 +++++++++++------- exchanges/order/futures_test.go | 41 ++-- exchanges/order/futures_types.go | 149 +++++++++----- exchanges/order/order_types.go | 2 - 10 files changed, 317 insertions(+), 194 deletions(-) diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index 4a811ebea30..cc988b07412 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -21,10 +21,10 @@ type Manager struct { // Snapshot consists of the timestamp the snapshot is from, along with all orders made // up until that time type Snapshot struct { - Offset int64 `json:"offset"` - Timestamp time.Time `json:"timestamp"` - Orders []SnapshotOrder `json:"orders"` - FuturesTracker *order.PositionController `json:"futures-tracker"` + Offset int64 `json:"offset"` + Timestamp time.Time `json:"timestamp"` + Orders []SnapshotOrder `json:"orders"` + FuturesTracker *order.PositionTrackerController `json:"futures-tracker"` } // SnapshotOrder adds some additional data that's only relevant for backtesting diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 6b77e87e856..b56a1dbe556 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -550,13 +550,13 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla } if snapshot.FuturesTracker == nil { - snapshot.FuturesTracker, err = gctorder.SetupPositionController(&gctorder.PositionControllerSetup{ - Exchange: e.GetExchange(), - Asset: e.GetAssetType(), - Pair: e.Pair(), - Underlying: e.Pair().Base, - OfflineCalculation: true, - PNLCalculator: settings.Exchange, + snapshot.FuturesTracker, err = gctorder.SetupPositionTrackerController(&gctorder.PositionControllerSetup{ + Exchange: e.GetExchange(), + Asset: e.GetAssetType(), + Pair: e.Pair(), + Underlying: e.Pair().Base, + OfflineCalculation: true, + ExchangePNLCalculation: settings.Exchange, }) if err != nil { return err diff --git a/engine/order_manager.go b/engine/order_manager.go index 9a5c60be89d..473c0d4cb5d 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -34,10 +34,11 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i return &OrderManager{ shutdown: make(chan struct{}), orderStore: store{ - Orders: make(map[string][]*order.Detail), - exchangeManager: exchangeManager, - commsManager: communicationsManager, - wg: wg, + Orders: make(map[string][]*order.Detail), + exchangeManager: exchangeManager, + commsManager: communicationsManager, + wg: wg, + futuresPositionTrackers: order.SetupPositionControllerReal(), }, verbose: verbose, }, nil @@ -835,6 +836,12 @@ func (s *store) updateExisting(od *order.Detail) error { for x := range r { if r[x].ID == od.ID { r[x].UpdateOrderFromDetail(od) + if r[x].AssetType.IsFutures() { + err := s.futuresPositionTrackers.TrackNewOrder(r[x]) + if err != nil { + return err + } + } return nil } } @@ -854,6 +861,12 @@ func (s *store) modifyExisting(id string, mod *order.Modify) error { for x := range r { if r[x].ID == id { r[x].UpdateOrderFromModify(mod) + if r[x].AssetType.IsFutures() { + err := s.futuresPositionTrackers.TrackNewOrder(r[x]) + if err != nil { + return err + } + } return nil } } @@ -890,6 +903,12 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) OrderDetails: r[x].Copy(), IsNewOrder: false, } + if od.AssetType.IsFutures() { + err = s.futuresPositionTrackers.TrackNewOrder(od) + if err != nil { + return nil, err + } + } return resp, nil } } @@ -900,6 +919,12 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) OrderDetails: od.Copy(), IsNewOrder: true, } + if od.AssetType.IsFutures() { + err = s.futuresPositionTrackers.TrackNewOrder(od) + if err != nil { + return nil, err + } + } return resp, nil } @@ -969,6 +994,9 @@ func (s *store) add(det *order.Detail) error { orders = append(orders, det) s.Orders[strings.ToLower(det.Exchange)] = orders + if det.AssetType.IsFutures() { + return s.futuresPositionTrackers.TrackNewOrder(det) + } return nil } diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index 3f12d82533d..58724805a51 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -37,12 +37,12 @@ type orderManagerConfig struct { // store holds all orders by exchange type store struct { - m sync.RWMutex - Orders map[string][]*order.Detail - commsManager iCommsManager - exchangeManager iExchangeManager - wg *sync.WaitGroup - futuresTrackers map[string]order.PositionController + m sync.RWMutex + Orders map[string][]*order.Detail + commsManager iCommsManager + exchangeManager iExchangeManager + wg *sync.WaitGroup + futuresPositionTrackers *order.PositionController } // OrderManager processes and stores orders across enabled exchanges diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 3261e45062e..91a01f0e930 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1784,13 +1784,14 @@ func TestCalculatePNLFromOrders(t *testing.T) { exch := f.Name item := asset.Futures setup := &order.PositionControllerSetup{ - Exchange: exch, - Asset: item, - Pair: pair, - Underlying: pair.Base, - PNLCalculator: &f, - } - p, err := order.SetupPositionController(setup) + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + ExchangePNLCalculation: &f, + UseExchangePNLCalculation: false, + } + p, err := order.SetupPositionTrackerController(setup) if err != nil { t.Error(err) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 6e176e74937..78309ffd5ce 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1278,41 +1278,39 @@ func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice floa } func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { - var result order.PNLResult if pnl.ExchangeBasedCalculation == nil { - return nil, order.ErrAmountExceedsMax + return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } + var result order.PNLResult if pnl.ExchangeBasedCalculation.CalculateOffline { uPNL := f.CalculateUnrealisedPNL(pnl.ExchangeBasedCalculation.Amount, pnl.ExchangeBasedCalculation.CurrentPrice, pnl.ExchangeBasedCalculation.PreviousPrice) result.UnrealisedPNL = decimal.NewFromFloat(uPNL) return &result, nil - } else { - ctx := context.Background() - info, err := f.GetAccountInfo(ctx) - if err != nil { - return nil, err + } + + info, err := f.GetAccountInfo(context.Background()) + if err != nil { + return nil, err + } + if info.Liquidating || info.Collateral == 0 { + return &result, order.ErrPositionLiquidated + } + for i := range info.Positions { + ftxSide := order.Side(info.Positions[i].Side) + var pnlSide order.Side + switch { + case ftxSide.IsShort(): + pnlSide = order.Short + case ftxSide.IsLong(): + pnlSide = order.Long + default: + return nil, order.ErrSideIsInvalid } - if info.Liquidating || info.Collateral == 0 { - // result.IsLiquidated = true + if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice && pnl.ExchangeBasedCalculation.Side == pnlSide { + result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPnL) + result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPnL) return &result, nil } - for i := range info.Positions { - ftxSide := order.Side(info.Positions[i].Side) - var pnlSide order.Side - switch ftxSide { - case order.Sell: - pnlSide = order.Short - case order.Buy: - pnlSide = order.Long - default: - return nil, order.ErrSideIsInvalid - } - if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice && pnl.ExchangeBasedCalculation.Side == pnlSide { - result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPnL) - result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPnL) - return &result, nil - } - } } return &result, nil } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b62f34732c8..537eadeb12d 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -9,11 +9,41 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/log" ) -// SetupPositionController creates a futures order tracker for a specific exchange -func SetupPositionController(setup *PositionControllerSetup) (*PositionController, error) { +func SetupPositionControllerReal() *PositionController { + return &PositionController{ + positionTrackerControllers: make(map[string]map[asset.Item]map[currency.Pair]*PositionTrackerController), + } +} + +func (c *PositionController) TrackNewOrder(d *Detail) error { + if !d.AssetType.IsFutures() { + return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, errNotFutureAsset) + } + if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)]; !ok { + c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*PositionTrackerController) + } + if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType]; !ok { + c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType] = make(map[currency.Pair]*PositionTrackerController) + } + if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair]; !ok { + ptc, err := SetupPositionTrackerController(&PositionControllerSetup{ + Exchange: strings.ToLower(d.Exchange), + Asset: d.AssetType, + Pair: d.Pair, + Underlying: d.Pair.Base, + }) + if err != nil { + return err + } + c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] = ptc + } + return c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair].TrackNewOrder(d) +} + +// SetupPositionTrackerController creates a futures order tracker for a specific exchange +func SetupPositionTrackerController(setup *PositionControllerSetup) (*PositionTrackerController, error) { if setup == nil { return nil, errNilSetup } @@ -29,27 +59,28 @@ func SetupPositionController(setup *PositionControllerSetup) (*PositionControlle if setup.Underlying.IsEmpty() { return nil, errEmptyUnderlying } - if setup.PNLCalculator == nil { + if setup.ExchangePNLCalculation == nil && setup.UseExchangePNLCalculation { return nil, errMissingPNLCalculationFunctions } - return &PositionController{ - exchange: strings.ToLower(setup.Exchange), - asset: setup.Asset, - pair: setup.Pair, - underlying: setup.Underlying, - pnlCalculation: setup.PNLCalculator, - offlinePNLCalculation: setup.OfflineCalculation, - orderPositions: make(map[string]*PositionTracker), + return &PositionTrackerController{ + exchange: strings.ToLower(setup.Exchange), + asset: setup.Asset, + pair: setup.Pair, + underlying: setup.Underlying, + offlinePNLCalculation: setup.OfflineCalculation, + orderPositions: make(map[string]*PositionTracker), + useExchangePNLCalculations: setup.UseExchangePNLCalculation, + exchangePNLCalculation: setup.ExchangePNLCalculation, }, nil } -func (e *PositionController) GetPositions() []*PositionTracker { +func (e *PositionTrackerController) GetPositions() []*PositionTracker { return e.positions } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices -func (e *PositionController) TrackNewOrder(d *Detail) error { +func (e *PositionTrackerController) TrackNewOrder(d *Detail) error { if d == nil { return ErrSubmissionIsNil } @@ -76,7 +107,14 @@ func (e *PositionController) TrackNewOrder(d *Detail) error { return nil } } - tracker, err := e.SetupPositionTracker(d.AssetType, d.Pair, d.Pair.Base, d.Price, d.Side) + setup := &PositionTrackerSetup{ + Pair: d.Pair, + EntryPrice: d.Price, + Underlying: d.Pair.Base, + Asset: d.AssetType, + Side: d.Side, + } + tracker, err := e.SetupPositionTracker(setup) if err != nil { return err } @@ -92,30 +130,34 @@ func (e *PositionController) TrackNewOrder(d *Detail) error { // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed -func (e *PositionController) SetupPositionTracker(item asset.Item, pair currency.Pair, underlying currency.Code, entryPrice float64, direction Side) (*PositionTracker, error) { +func (e *PositionTrackerController) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { if e.exchange == "" { return nil, errExchangeNameEmpty } - if !item.IsValid() || !item.IsFutures() { + if setup == nil { + return nil, errNilSetup + } + if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { return nil, errNotFutureAsset } - if pair.IsEmpty() { + if !setup.Pair.IsEmpty() { return nil, ErrPairIsEmpty } resp := &PositionTracker{ exchange: strings.ToLower(e.exchange), - asset: item, - contractPair: pair, - underlyingAsset: underlying, + asset: setup.Asset, + contractPair: setup.Pair, + underlyingAsset: setup.Underlying, status: Open, - pnlCalculation: e.pnlCalculation, - entryPrice: decimal.NewFromFloat(entryPrice), - currentDirection: direction, + entryPrice: decimal.NewFromFloat(setup.EntryPrice), + currentDirection: setup.Side, } - if e.pnlCalculation == nil { - log.Warnf(log.OrderMgr, "no pnl calculation functions supplied for %v %v %v, using default calculations", e.exchange, e.asset, e.pair) - e.pnlCalculation = resp + if !e.useExchangePNLCalculations { + // use position tracker's pnl calculation by default + resp.PNLManagement = resp + } else { + resp.PNLManagement = e.exchangePNLCalculation } return resp, nil } @@ -126,16 +168,17 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro defer func() { p.latestPrice = decimal.NewFromFloat(currentPrice) }() - pnl, err := p.pnlCalculation.CalculatePNL(&PNLCalculator{ + pnl, err := p.PNLManagement.CalculatePNL(&PNLCalculator{ TimeBasedCalculation: &TimeBasedCalculation{ - currentPrice, + Time: t, + CurrentPrice: currentPrice, }, }) if err != nil { return err } return p.UpsertPNLEntry(PNLResult{ - Time: t, + Time: pnl.Time, UnrealisedPNL: pnl.UnrealisedPNL, RealisedPNL: pnl.RealisedPNL, }) @@ -215,9 +258,21 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.currentDirection = d.Side } - result, err := p.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) + result, err := p.PNLManagement.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) if err != nil { - return err + if errors.Is(err, ErrPositionLiquidated) { + err = p.UpsertPNLEntry(*result) + if err != nil { + return err + } + // can you go into debt? + p.realisedPNL = decimal.Zero + p.unrealisedPNL = decimal.Zero + p.status = Liquidation + // return p.liquidate() + } else { + return err + } } err = p.UpsertPNLEntry(*result) if err != nil { @@ -261,40 +316,22 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) result.Time = calc.OrderBasedCalculation.Date price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) - if (p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && p.exposure.LessThan(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)) { - // we need to handle the pnl twice as we're flipping direction - result2 := &PNLResult{ - Time: result.Time, - } - one := p.exposure.Sub(amount).Abs() - result, err = p.idkYet(calc, result, one, price) + result.UnrealisedPNL, err = p.calculatePNLAndSwapSides(calc, one, price) if err != nil { - return nil, err - } - p.unrealisedPNL = result.UnrealisedPNL - if calc.OrderBasedCalculation.Side.IsShort() { - calc.OrderBasedCalculation.Side = Long - } else if calc.OrderBasedCalculation.Side.IsLong() { - calc.OrderBasedCalculation.Side = Short + return result, err } two := amount.Sub(p.exposure).Abs() - result2, err = p.idkYet(calc, result2, two, price) + result.UnrealisedPNL, err = p.calculatePNLAndSwapSides(calc, two, price) if err != nil { - return nil, err - } - p.unrealisedPNL = result2.UnrealisedPNL - if calc.OrderBasedCalculation.Side.IsShort() { - calc.OrderBasedCalculation.Side = Long - } else if calc.OrderBasedCalculation.Side.IsLong() { - calc.OrderBasedCalculation.Side = Short + return result, err } - return result2, nil + return result, nil } - result, err = p.idkYet(calc, result, amount, price) + result.UnrealisedPNL, err = p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Side, amount, price) if err != nil { return nil, err } @@ -309,19 +346,36 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) return nil, errMissingPNLCalculationFunctions } -func (p *PositionTracker) idkYet(calc *PNLCalculator, result *PNLResult, amount decimal.Decimal, price decimal.Decimal) (*PNLResult, error) { +// calculatePNLAndSwapSides is used to help calculate PNL when +// a new position flips the direction without closing the overall position +func (p *PositionTracker) calculatePNLAndSwapSides(calc *PNLCalculator, value, price decimal.Decimal) (decimal.Decimal, error) { + result, err := p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Side, value, price) + if err != nil { + return result, err + } + p.unrealisedPNL = result + if calc.OrderBasedCalculation.Side.IsShort() { + calc.OrderBasedCalculation.Side = Long + } else if calc.OrderBasedCalculation.Side.IsLong() { + calc.OrderBasedCalculation.Side = Short + } + return result, nil +} + +func (p *PositionTracker) calculateUnrealisedPNL(side Side, amount, price decimal.Decimal) (decimal.Decimal, error) { hello := amount.Mul(price) + var resp decimal.Decimal switch { - case p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsShort(), - p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsLong(): - result.UnrealisedPNL = p.unrealisedPNL.Add(hello) - case p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong(), - p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort(): - result.UnrealisedPNL = p.unrealisedPNL.Sub(hello) + case p.currentDirection.IsShort() && side.IsShort(), + p.currentDirection.IsLong() && side.IsLong(): + resp = p.unrealisedPNL.Add(hello) + case p.currentDirection.IsShort() && side.IsLong(), + p.currentDirection.IsLong() && side.IsShort(): + resp = p.unrealisedPNL.Sub(hello) default: - return nil, fmt.Errorf("%v %v %v %v whats wrong", p.currentDirection, calc.OrderBasedCalculation.Side, result.UnrealisedPNL, amount.Mul(price)) + return resp, fmt.Errorf("%v %v %v %v whats wrong", p.currentDirection, side, resp, hello) } - return result, nil + return resp, nil } func (p *PositionTracker) TrackPrice(price decimal.Decimal) decimal.Decimal { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 8f99519da87..0c5faa1297a 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -33,10 +33,11 @@ func TestTrackPNL(t *testing.T) { fPNL := &FakePNL{ result: &PNLResult{}, } - e := PositionController{ - exchange: exch, - pnlCalculation: fPNL, + e := PositionTrackerController{ + exchange: exch, + exchangePNLCalculation: fPNL, } + setup := &PositionControllerSetup{} f, err := e.SetupPositionTracker(item, pair, pair.Base) if !errors.Is(err, nil) { t.Error(err) @@ -85,9 +86,9 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - e := PositionController{ - exchange: "test", - pnlCalculation: &FakePNL{}, + e := PositionTrackerController{ + exchange: "test", + exchangePNLCalculation: &FakePNL{}, } f, err := e.SetupPositionTracker(item, pair, pair.Base) if !errors.Is(err, nil) { @@ -194,41 +195,41 @@ func TestTrackNewOrder(t *testing.T) { func TestSetupFuturesTracker(t *testing.T) { t.Parallel() - _, err := SetupPositionController(nil) + _, err := SetupPositionTrackerController(nil) if !errors.Is(err, errNilSetup) { t.Error(err) } setup := &PositionControllerSetup{} - _, err = SetupPositionController(setup) + _, err = SetupPositionTrackerController(setup) if !errors.Is(err, errExchangeNameEmpty) { t.Error(err) } setup.Exchange = "test" - _, err = SetupPositionController(setup) + _, err = SetupPositionTrackerController(setup) if !errors.Is(err, errNotFutureAsset) { t.Error(err) } setup.Asset = asset.Futures - _, err = SetupPositionController(setup) + _, err = SetupPositionTrackerController(setup) if !errors.Is(err, ErrPairIsEmpty) { t.Error(err) } setup.Pair = currency.NewPair(currency.BTC, currency.USDT) - _, err = SetupPositionController(setup) + _, err = SetupPositionTrackerController(setup) if !errors.Is(err, errEmptyUnderlying) { t.Error(err) } setup.Underlying = currency.BTC - _, err = SetupPositionController(setup) + _, err = SetupPositionTrackerController(setup) if !errors.Is(err, errMissingPNLCalculationFunctions) { t.Error(err) } - setup.PNLCalculator = &FakePNL{} - resp, err := SetupPositionController(setup) + setup.ExchangePNLCalculation = &FakePNL{} + resp, err := SetupPositionTrackerController(setup) if !errors.Is(err, nil) { t.Error(err) } @@ -243,13 +244,13 @@ func TestExchangeTrackNewOrder(t *testing.T) { item := asset.Futures pair := currency.NewPair(currency.BTC, currency.USDT) setup := &PositionControllerSetup{ - Exchange: exch, - Asset: item, - Pair: pair, - Underlying: pair.Base, - PNLCalculator: &FakePNL{}, + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + ExchangePNLCalculation: &FakePNL{}, } - resp, err := SetupPositionController(setup) + resp, err := SetupPositionTrackerController(setup) if !errors.Is(err, nil) { t.Error(err) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 00256c0011d..b5e1d1b9dc0 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -21,17 +21,103 @@ var ( errEmptyUnderlying = errors.New("underlying asset unset") errNoPositions = errors.New("there are no positions") errNilSetup = errors.New("nil setup received") + // ErrNilPNLCalculator is raised when pnl calculation is requested for + // an exchange, but the fields are not set properly + ErrNilPNLCalculator = errors.New("nil pnl calculator received") + // ErrPositionLiquidated is raised when checking PNL status only for + // it to be liquidated + ErrPositionLiquidated = errors.New("position liquidated") ) +// PNLManagement is an interface to allow multiple +// ways of calculating PNL to be used for futures positions type PNLManagement interface { CalculatePNL(*PNLCalculator) (*PNLResult, error) } +// CollateralManagement is an interface that allows +// multiple ways of calculating the size of collateral +// on an exchange type CollateralManagement interface { ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) CalculateTotalCollateral([]CollateralCalculator) (decimal.Decimal, error) } +type PositionController struct { + positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*PositionTrackerController +} + +// PositionTrackerController will track the performance of +// futures positions over time. If an old position tracker +// is closed, then the position controller will create a new one +// to track the current positions +type PositionTrackerController struct { + exchange string + asset asset.Item + pair currency.Pair + underlying currency.Code + positions []*PositionTracker + // order positions allows for an easier time knowing which order is + // part of which position tracker + orderPositions map[string]*PositionTracker + pnl decimal.Decimal + offlinePNLCalculation bool + useExchangePNLCalculations bool + exchangePNLCalculation PNLManagement +} + +// PositionControllerSetup holds the parameters +// required to set up a position controller +type PositionControllerSetup struct { + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + OfflineCalculation bool + UseExchangePNLCalculation bool + ExchangePNLCalculation PNLManagement +} + +// PositionTracker tracks futures orders until the overall position is considered closed +// eg a user can open a short position, append to it via two more shorts, reduce via a small long and +// finally close off the remainder via another long. All of these actions are to be +// captured within one position tracker. It allows for a user to understand their PNL +// specifically for futures positions. Utilising spot/futures arbitrage will not be tracked +// completely within this position tracker, however, can still provide a good +// timeline of performance until the position is closed +type PositionTracker struct { + exchange string + asset asset.Item + contractPair currency.Pair + underlyingAsset currency.Code + exposure decimal.Decimal + currentDirection Side + status Status + averageLeverage decimal.Decimal + unrealisedPNL decimal.Decimal + realisedPNL decimal.Decimal + shortPositions []Detail + longPositions []Detail + pnlHistory []PNLResult + entryPrice decimal.Decimal + closingPrice decimal.Decimal + offlinePNLCalculation bool + PNLManagement + latestPrice decimal.Decimal +} + +type PositionTrackerSetup struct { + Pair currency.Pair + EntryPrice float64 + Underlying currency.Code + Asset asset.Item + Side Side +} + +// CollateralCalculator is used to determine +// the size of collateral holdings for an exchange +// eg on FTX, the collateral is scaled depending on what +// currency it is type CollateralCalculator struct { CollateralCurrency currency.Code Asset asset.Item @@ -40,16 +126,25 @@ type CollateralCalculator struct { USDPrice decimal.Decimal } +// PNLCalculator is used to calculate PNL values +// for an open position type PNLCalculator struct { OrderBasedCalculation *Detail TimeBasedCalculation *TimeBasedCalculation ExchangeBasedCalculation *ExchangeBasedCalculation } +// TimeBasedCalculation will update PNL values +// based on the current time type TimeBasedCalculation struct { + Time time.Time CurrentPrice float64 } +// ExchangeBasedCalculation are the fields required to +// calculate PNL using an exchange's custom PNL calculations +// eg FTX uses a different method than Binance to calculate PNL +// values type ExchangeBasedCalculation struct { CalculateOffline bool Underlying currency.Code @@ -63,61 +158,9 @@ type ExchangeBasedCalculation struct { PreviousPrice float64 } +// PNLResult stores pnl history at a point in time type PNLResult struct { Time time.Time UnrealisedPNL decimal.Decimal RealisedPNL decimal.Decimal } - -// PositionController will track the performance of -// po -type PositionController struct { - exchange string - asset asset.Item - pair currency.Pair - underlying currency.Code - positions []*PositionTracker - orderPositions map[string]*PositionTracker - pnl decimal.Decimal - pnlCalculation PNLManagement - offlinePNLCalculation bool -} - -// PositionTracker tracks futures orders until the overall position is considered closed -// eg a user can open a short position, append to it via two more shorts, reduce via a small long and -// finally close off the remainder via another long. All of these actions are to be -// captured within one position tracker. It allows for a user to understand their PNL -// specifically for futures positions. Utilising spot/futures arbitrage will not be tracked -// completely within this position tracker, however, can still provide a good -// timeline of performance until the position is closed -type PositionTracker struct { - exchange string - asset asset.Item - contractPair currency.Pair - underlyingAsset currency.Code - exposure decimal.Decimal - currentDirection Side - status Status - averageLeverage decimal.Decimal - unrealisedPNL decimal.Decimal - realisedPNL decimal.Decimal - shortPositions []Detail - longPositions []Detail - pnlHistory []PNLResult - entryPrice decimal.Decimal - closingPrice decimal.Decimal - offlinePNLCalculation bool - pnlCalculation PNLManagement - latestPrice decimal.Decimal -} - -// PositionControllerSetup holds the parameters -// required to set up a position controller -type PositionControllerSetup struct { - Exchange string - Asset asset.Item - Pair currency.Pair - Underlying currency.Code - OfflineCalculation bool - PNLCalculator PNLManagement -} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index fce4a19ab40..5ef124dbe90 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -4,7 +4,6 @@ import ( "errors" "time" - "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -147,7 +146,6 @@ type Detail struct { Type Type Side Side Status Status - UnrealisedPNL decimal.Decimal AssetType asset.Item Date time.Time CloseTime time.Time From fa48e4d9c6d1a5585a5b2528109362f8ebafb5dc Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 20 Dec 2021 15:06:20 +1100 Subject: [PATCH 042/171] Backtester now compiles. Adds test coverage --- backtester/backtest/backtest.go | 67 ++--- backtester/config/config_types.go | 1 + ...a-api-candles-exchange-level-funding.strat | 11 +- .../dca-api-candles-multiple-currencies.strat | 11 +- ...-api-candles-simultaneous-processing.strat | 11 +- .../config/examples/dca-api-candles.strat | 8 +- .../config/examples/dca-api-trades.strat | 8 +- .../config/examples/dca-candles-live.strat | 8 +- .../config/examples/dca-csv-candles.strat | 8 +- .../config/examples/dca-csv-trades.strat | 8 +- .../examples/dca-database-candles.strat | 8 +- .../config/examples/futures-api-candles.strat | 29 ++- .../config/examples/rsi-api-candles.strat | 11 +- .../t2b2-api-candles-exchange-funding.strat | 23 +- .../eventhandlers/exchange/exchange_types.go | 7 +- .../portfolio/compliance/compliance_types.go | 7 +- .../eventhandlers/portfolio/portfolio.go | 119 ++++++--- .../portfolio/portfolio_types.go | 15 +- engine/order_manager.go | 2 +- exchanges/exchange.go | 10 +- exchanges/ftx/ftx_test.go | 2 +- exchanges/ftx/ftx_wrapper.go | 19 ++ exchanges/order/futures.go | 59 +++-- exchanges/order/futures_test.go | 228 ++++++++++++++++-- exchanges/order/futures_types.go | 13 +- 25 files changed, 517 insertions(+), 176 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 3d0cbb6ce50..ed95f176272 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -555,21 +555,22 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange } } resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ - Exchange: cfg.CurrencySettings[i].ExchangeName, - MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, - MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, - Pair: pair, - Asset: a, - ExchangeFee: takerFee, - MakerFee: takerFee, - TakerFee: makerFee, - UseRealOrders: realOrders, - BuySide: buyRule, - SellSide: sellRule, - Leverage: lev, - Limits: limits, - SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, - CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, + Exchange: cfg.CurrencySettings[i].ExchangeName, + MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, + MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, + Pair: pair, + Asset: a, + ExchangeFee: takerFee, + MakerFee: takerFee, + TakerFee: makerFee, + UseRealOrders: realOrders, + BuySide: buyRule, + SellSide: sellRule, + Leverage: lev, + Limits: limits, + SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, + CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, + UseExchangePNLCalculation: cfg.CurrencySettings[i].UseExchangePNLCalculation, }) } } @@ -1019,13 +1020,13 @@ func (bt *BackTest) processSimultaneousDataEvents() error { // too much bad data is a severe error and backtesting must cease return err } - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "OnSimultaneousSignals %v", err) return nil } for i := range signals { err = bt.Statistic.SetEventForOffset(signals[i]) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", signals[i].GetExchange(), signals[i].GetAssetType(), signals[i].Pair(), err) } bt.EventQueue.AppendEvent(signals[i]) } @@ -1041,23 +1042,27 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if err == statistics.ErrAlreadyProcessed { return err } - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } // update portfolio manager with the latest price err = bt.Portfolio.UpdateHoldings(ev, funds) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "UpdateHoldings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } - if ev.GetAssetType() == asset.Futures { + if ev.GetAssetType().IsFutures() { var cr funding.ICollateralReleaser cr, err = funds.GetCollateralReleaser() if err != nil { return err } - err = bt.Portfolio.CalculatePNL(ev, cr) + err = bt.Portfolio.CalculatePNL(ev) if err != nil { - log.Error(log.BackTester, err) + if errors.Is(err, gctorder.ErrPositionLiquidated) { + cr.Liquidate() + } else { + log.Errorf(log.BackTester, "CalculatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } } } @@ -1068,18 +1073,18 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) { cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } var o *order.Order o, err = bt.Portfolio.OnSignal(ev, &cs, funds) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } err = bt.Statistic.SetEventForOffset(o) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(o) @@ -1097,7 +1102,7 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease } err = bt.Statistic.SetEventForOffset(f) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(f) } @@ -1105,13 +1110,13 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) { t, err := bt.Portfolio.OnFill(ev, funds) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } err = bt.Statistic.SetEventForOffset(t) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var holding *holdings.Holding @@ -1124,20 +1129,20 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) } else { err = bt.Statistic.AddHoldingsForTime(holding) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } var cp *compliance.Manager cp, err = bt.Portfolio.GetComplianceManager(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "GetComplianceManager %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } snap := cp.GetLatestSnapshot() err = bt.Statistic.AddComplianceSnapshotForTime(snap, ev) if err != nil { - log.Error(log.BackTester, err) + log.Errorf(log.BackTester, "AddComplianceSnapshotForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 1dcbac296ea..28f2065bcca 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -143,6 +143,7 @@ type CurrencySettings struct { CanUseExchangeLimits bool `json:"use-exchange-order-limits"` ShowExchangeOrderLimitWarning bool `json:"-"` + UseExchangePNLCalculation bool `json:"use-exchange-pnl-calculation"` } type SpotDetails struct { diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index b6874f002f4..e2f3112b0ff 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -39,7 +39,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -62,7 +63,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -77,8 +79,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles-multiple-currencies.strat b/backtester/config/examples/dca-api-candles-multiple-currencies.strat index b8a7ab5b60c..17ba2aa1ef6 100644 --- a/backtester/config/examples/dca-api-candles-multiple-currencies.strat +++ b/backtester/config/examples/dca-api-candles-multiple-currencies.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -58,7 +59,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -73,8 +75,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat index 9af74be31f6..fd2229b8b01 100644 --- a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat +++ b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -58,7 +59,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -73,8 +75,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles.strat b/backtester/config/examples/dca-api-candles.strat index 510550a3b0e..56618d23235 100644 --- a/backtester/config/examples/dca-api-candles.strat +++ b/backtester/config/examples/dca-api-candles.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -47,8 +48,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-trades.strat b/backtester/config/examples/dca-api-trades.strat index 6610b791736..9606f67af81 100644 --- a/backtester/config/examples/dca-api-trades.strat +++ b/backtester/config/examples/dca-api-trades.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": true, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -47,8 +48,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.1", diff --git a/backtester/config/examples/dca-candles-live.strat b/backtester/config/examples/dca-candles-live.strat index ef809d5befc..b6fe2ed7a3f 100644 --- a/backtester/config/examples/dca-candles-live.strat +++ b/backtester/config/examples/dca-candles-live.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -50,8 +51,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-csv-candles.strat b/backtester/config/examples/dca-csv-candles.strat index c5e1d1a345d..0ffb69bc139 100644 --- a/backtester/config/examples/dca-csv-candles.strat +++ b/backtester/config/examples/dca-csv-candles.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -45,8 +46,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-csv-trades.strat b/backtester/config/examples/dca-csv-trades.strat index 5ef096fcc3f..7b4f43c7600 100644 --- a/backtester/config/examples/dca-csv-trades.strat +++ b/backtester/config/examples/dca-csv-trades.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -45,8 +46,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0", diff --git a/backtester/config/examples/dca-database-candles.strat b/backtester/config/examples/dca-database-candles.strat index 37548ce3f52..d5300fc4f97 100644 --- a/backtester/config/examples/dca-database-candles.strat +++ b/backtester/config/examples/dca-database-candles.strat @@ -32,7 +32,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -61,8 +62,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/futures-api-candles.strat index 3a246a24784..f3dd4d8db42 100644 --- a/backtester/config/examples/futures-api-candles.strat +++ b/backtester/config/examples/futures-api-candles.strat @@ -26,16 +26,21 @@ "futures-details": { "leverage": { "can-use-leverage": true, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 1 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 1, + "maximum-collateral-leverage-rate": 0 }, "collateral-currency": "USDT" }, "buy-side": { - "maximum-total": "1000" + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" }, "sell-side": { - "maximum-total": "1000" + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" }, "min-slippage-percent": "0", "max-slippage-percent": "0", @@ -43,7 +48,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -58,14 +64,19 @@ "portfolio-settings": { "leverage": { "can-use-leverage": true, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { - "maximum-total": "1000" + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" }, "sell-side": { - "maximum-total": "1000" + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" } }, "statistic-settings": { diff --git a/backtester/config/examples/rsi-api-candles.strat b/backtester/config/examples/rsi-api-candles.strat index 80c9693ed55..7fce38b2696 100644 --- a/backtester/config/examples/rsi-api-candles.strat +++ b/backtester/config/examples/rsi-api-candles.strat @@ -37,7 +37,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -64,7 +65,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -79,8 +81,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 92a5e4385dc..8ac70c69026 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -52,7 +52,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -75,7 +76,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -98,7 +100,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -121,7 +124,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -144,7 +148,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false }, { "exchange-name": "binance", @@ -167,7 +172,8 @@ "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, - "use-exchange-order-limits": false + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { @@ -182,8 +188,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": "0", - "maximum-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": 0, + "maximum-leverage-rate": 0, + "maximum-collateral-leverage-rate": 0 }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index af582cdeacb..6a502cbdb03 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -54,9 +54,10 @@ type Settings struct { MinimumSlippageRate decimal.Decimal MaximumSlippageRate decimal.Decimal - Limits *gctorder.Limits - CanUseExchangeLimits bool - SkipCandleVolumeFitting bool + Limits *gctorder.Limits + CanUseExchangeLimits bool + SkipCandleVolumeFitting bool + UseExchangePNLCalculation bool } // MinMax are the rules which limit the placement of orders. diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_types.go b/backtester/eventhandlers/portfolio/compliance/compliance_types.go index cc988b07412..afa7168d986 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_types.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_types.go @@ -21,10 +21,9 @@ type Manager struct { // Snapshot consists of the timestamp the snapshot is from, along with all orders made // up until that time type Snapshot struct { - Offset int64 `json:"offset"` - Timestamp time.Time `json:"timestamp"` - Orders []SnapshotOrder `json:"orders"` - FuturesTracker *order.PositionTrackerController `json:"futures-tracker"` + Offset int64 `json:"offset"` + Timestamp time.Time `json:"timestamp"` + Orders []SnapshotOrder `json:"orders"` } // SnapshotOrder adds some additional data that's only relevant for backtesting diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b56a1dbe556..9b57161874e 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -16,6 +16,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -143,6 +144,9 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi USDPrice: ev.GetPrice(), }) if err != nil { + if errors.Is(err, gctcommon.ErrFunctionNotSupported) { + + } return nil, err } @@ -334,12 +338,6 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { } snapOrder.Order = fo prevSnap.Orders = append(prevSnap.Orders, snapOrder) - if fo.AssetType == asset.Futures { - err = prevSnap.FuturesTracker.TrackNewOrder(fillEvent.GetOrder()) - if err != nil { - return err - } - } } snap := &compliance.Snapshot{ Offset: fillEvent.GetOffset(), @@ -497,6 +495,24 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch g if _, ok := p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair]; ok { return nil } + + var pnl gctorder.PNLManagement + if settings.UseExchangePNLCalculation { + pnl = exch + } + ptc, err := gctorder.SetupMultiPositionTracker(&gctorder.PositionControllerSetup{ + Exchange: exch.GetName(), + Asset: settings.Asset, + Pair: settings.Pair, + Underlying: settings.Pair.Base, + OfflineCalculation: !settings.UseRealOrders, + UseExchangePNLCalculation: settings.UseExchangePNLCalculation, + ExchangePNLCalculation: pnl, + }) + if err != nil { + return fmt.Errorf("unable to setup currency settings %w", err) + } + p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair] = &Settings{ Fee: settings.ExchangeFee, BuySideSizing: settings.BuySide, @@ -505,7 +521,8 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch g ComplianceManager: compliance.Manager{ Snapshots: []compliance.Snapshot{}, }, - Exchange: exch, + Exchange: exch, + FuturesTracker: ptc, } return nil } @@ -535,7 +552,7 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // CalculatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL -func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.ICollateralReleaser) error { +func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { if !e.GetAssetType().IsFutures() { return nil // or err } @@ -549,31 +566,79 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler, funds funding.IColla return err } - if snapshot.FuturesTracker == nil { - snapshot.FuturesTracker, err = gctorder.SetupPositionTrackerController(&gctorder.PositionControllerSetup{ - Exchange: e.GetExchange(), - Asset: e.GetAssetType(), - Pair: e.Pair(), - Underlying: e.Pair().Base, - OfflineCalculation: true, - ExchangePNLCalculation: settings.Exchange, - }) + for i := range snapshot.Orders { + err = settings.FuturesTracker.TrackNewOrder(snapshot.Orders[i].Order) if err != nil { return err } } + pnl, err := p.GetLatestPNLForEvent(e) + if err != nil { + return err + } + log.Infof(log.BackTester, "%+v", pnl) + return nil +} - for i := range snapshot.Orders { - err = snapshot.FuturesTracker.TrackNewOrder(snapshot.Orders[i].Order) - if err != nil { - return err - } +// GetLatestPNLForEvent takes in an event and returns the latest PNL data +// if it exists +func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, error) { + if !e.GetAssetType().IsFutures() { + return nil, errors.New("not a future") + } + settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] + if !ok { + return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), errNoPortfolioSettings) } - _, err = snapshot.FuturesTracker.CalculateLatestPNL(&gctorder.PNLCalculator{ - MarkPrice: e.GetClosePrice().InexactFloat64(), - PrevMarkPrice: e.GetOpenPrice().InexactFloat64(), - }) + if settings.FuturesTracker == nil { + return nil, errors.New("no futures tracker") + } + positions := settings.FuturesTracker.GetPositions() + if positions == nil { + return nil, nil + } + pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() + if err != nil { + return nil, err + } - return nil + return &PNLSummary{ + Exchange: e.GetExchange(), + Item: e.GetAssetType(), + Pair: e.Pair(), + PNL: pnl, + }, nil +} + +// GetLatestPNLs returns all PNL details in one array +func (p *Portfolio) GetLatestPNLs() []PNLSummary { + var result []PNLSummary + for exchK := range p.exchangeAssetPairSettings { + for assetK := range p.exchangeAssetPairSettings[exchK] { + if !assetK.IsFutures() { + continue + } + for pairK, settings := range p.exchangeAssetPairSettings[exchK][assetK] { + if settings == nil { + continue + } + if settings.FuturesTracker == nil { + continue + } + positions := settings.FuturesTracker.GetPositions() + pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() + if err != nil { + continue + } + result = append(result, PNLSummary{ + Exchange: exchK, + Item: assetK, + Pair: pairK, + PNL: pnl, + }) + } + } + } + return result } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 6884829527d..4f4a7419331 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -16,6 +16,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) const notEnoughFundsTo = "not enough funds to" @@ -60,7 +61,9 @@ type Handler interface { SetFee(string, asset.Item, currency.Pair, decimal.Decimal) GetFee(string, asset.Item, currency.Pair) decimal.Decimal - CalculatePNL(common.DataEventHandler, funding.ICollateralReleaser) error + CalculatePNL(common.DataEventHandler) error + GetLatestPNLForEvent(handler common.EventHandler) (*PNLSummary, error) + GetLatestPNLs() []PNLSummary Reset() } @@ -80,4 +83,14 @@ type Settings struct { HoldingsSnapshots []holdings.Holding ComplianceManager compliance.Manager Exchange gctexchange.IBotExchange + FuturesTracker *gctorder.MultiPositionTracker +} + +// PNLSummary holds a PNL result along with +// exchange details +type PNLSummary struct { + Exchange string + Item asset.Item + Pair currency.Pair + PNL gctorder.PNLResult } diff --git a/engine/order_manager.go b/engine/order_manager.go index 473c0d4cb5d..abe29fb4b2b 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -38,7 +38,7 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i exchangeManager: exchangeManager, commsManager: communicationsManager, wg: wg, - futuresPositionTrackers: order.SetupPositionControllerReal(), + futuresPositionTrackers: order.SetupPositionController(), }, verbose: verbose, }, nil diff --git a/exchanges/exchange.go b/exchanges/exchange.go index fe9e617b3b2..f572248252c 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1467,8 +1467,8 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ // It will also determine whether the position is considered to be liquidated // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset -func (b *Base) CalculatePNL(calc *order.PNLCalculator) (*order.PNLResult, error) { - return nil, common.ErrFunctionNotSupported +func (b *Base) CalculatePNL(*order.PNLCalculator) (*order.PNLResult, error) { + return nil, common.ErrNotYetImplemented } // ScaleCollateral is an overridable function to allow PNL to be calculated on an @@ -1476,10 +1476,10 @@ func (b *Base) CalculatePNL(calc *order.PNLCalculator) (*order.PNLResult, error) // It will also determine whether the position is considered to be liquidated // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset -func (b *Base) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { - return decimal.Zero, common.ErrFunctionNotSupported +func (b *Base) ScaleCollateral(*order.CollateralCalculator) (decimal.Decimal, error) { + return decimal.Zero, common.ErrNotYetImplemented } func (b *Base) CalculateTotalCollateral([]order.CollateralCalculator) (decimal.Decimal, error) { - return decimal.Zero, common.ErrFunctionNotSupported + return decimal.Zero, common.ErrNotYetImplemented } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 91a01f0e930..137833e740b 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1791,7 +1791,7 @@ func TestCalculatePNLFromOrders(t *testing.T) { ExchangePNLCalculation: &f, UseExchangePNLCalculation: false, } - p, err := order.SetupPositionTrackerController(setup) + p, err := order.SetupMultiPositionTracker(setup) if err != nil { t.Error(err) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 78309ffd5ce..d86dc4ccf0f 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1315,6 +1315,25 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { return &result, nil } +func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { + var result decimal.Decimal + collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] + if !ok { + return decimal.Zero, errCoinMustBeSpecified + } + if calc.CollateralAmount.IsPositive() { + if collateralWeight.IMFFactor == 0 { + return decimal.Zero, errCoinMustBeSpecified + } + what := decimal.NewFromFloat(collateralWeight.Total) + what2 := decimal.NewFromFloat(1.1 / collateralWeight.IMFFactor * math.Sqrt(calc.CollateralAmount.InexactFloat64())) + result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(what, what2))) + } else { + result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) + } + return result, nil +} + func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal for i := range collateralAssets { diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 537eadeb12d..4a7402617e6 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -11,24 +11,27 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) -func SetupPositionControllerReal() *PositionController { +func SetupPositionController() *PositionController { return &PositionController{ - positionTrackerControllers: make(map[string]map[asset.Item]map[currency.Pair]*PositionTrackerController), + positionTrackerControllers: make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker), } } func (c *PositionController) TrackNewOrder(d *Detail) error { + if d == nil { + return errNilOrder + } if !d.AssetType.IsFutures() { return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, errNotFutureAsset) } if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)]; !ok { - c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*PositionTrackerController) + c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) } if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType]; !ok { - c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType] = make(map[currency.Pair]*PositionTrackerController) + c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType] = make(map[currency.Pair]*MultiPositionTracker) } if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair]; !ok { - ptc, err := SetupPositionTrackerController(&PositionControllerSetup{ + ptc, err := SetupMultiPositionTracker(&PositionControllerSetup{ Exchange: strings.ToLower(d.Exchange), Asset: d.AssetType, Pair: d.Pair, @@ -42,8 +45,8 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair].TrackNewOrder(d) } -// SetupPositionTrackerController creates a futures order tracker for a specific exchange -func SetupPositionTrackerController(setup *PositionControllerSetup) (*PositionTrackerController, error) { +// SetupMultiPositionTracker creates a futures order tracker for a specific exchange +func SetupMultiPositionTracker(setup *PositionControllerSetup) (*MultiPositionTracker, error) { if setup == nil { return nil, errNilSetup } @@ -62,7 +65,7 @@ func SetupPositionTrackerController(setup *PositionControllerSetup) (*PositionTr if setup.ExchangePNLCalculation == nil && setup.UseExchangePNLCalculation { return nil, errMissingPNLCalculationFunctions } - return &PositionTrackerController{ + return &MultiPositionTracker{ exchange: strings.ToLower(setup.Exchange), asset: setup.Asset, pair: setup.Pair, @@ -74,13 +77,13 @@ func SetupPositionTrackerController(setup *PositionControllerSetup) (*PositionTr }, nil } -func (e *PositionTrackerController) GetPositions() []*PositionTracker { +func (e *MultiPositionTracker) GetPositions() []*PositionTracker { return e.positions } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices -func (e *PositionTrackerController) TrackNewOrder(d *Detail) error { +func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { if d == nil { return ErrSubmissionIsNil } @@ -130,7 +133,7 @@ func (e *PositionTrackerController) TrackNewOrder(d *Detail) error { // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed -func (e *PositionTrackerController) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { +func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { if e.exchange == "" { return nil, errExchangeNameEmpty } @@ -140,7 +143,7 @@ func (e *PositionTrackerController) SetupPositionTracker(setup *PositionTrackerS if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { return nil, errNotFutureAsset } - if !setup.Pair.IsEmpty() { + if setup.Pair.IsEmpty() { return nil, ErrPairIsEmpty } @@ -186,11 +189,18 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro func (p *PositionTracker) GetRealisedPNL() (decimal.Decimal, error) { if p.status != Closed { - return decimal.Zero, errors.New("position not closed") + return decimal.Zero, fmt.Errorf("%v %v %v %w", p.exchange, p.asset, p.contractPair, errPositionNotClosed) } return p.realisedPNL, nil } +func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { + if len(p.pnlHistory) == 0 { + return PNLResult{}, fmt.Errorf("%v %v %v %w", p.exchange, p.asset, p.contractPair, errNoPNLHistory) + } + return p.pnlHistory[len(p.pnlHistory)-1], nil +} + // TrackNewOrder knows how things are going for a given // futures contract func (p *PositionTracker) TrackNewOrder(d *Detail) error { @@ -215,6 +225,9 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if d.ID == "" { return ErrOrderIDNotSet } + if d.Date.IsZero() { + return fmt.Errorf("%w for %v %v %v order ID: %v unset", errTimeUnset, d.Exchange, d.AssetType, d.Pair, d.ID) + } if len(p.shortPositions) == 0 && len(p.longPositions) == 0 { p.entryPrice = decimal.NewFromFloat(d.Price) } @@ -268,7 +281,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { // can you go into debt? p.realisedPNL = decimal.Zero p.unrealisedPNL = decimal.Zero - p.status = Liquidation + p.status = Closed // return p.liquidate() } else { return err @@ -284,6 +297,8 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.currentDirection = Long } else if shortSide.GreaterThan(longSide) { p.currentDirection = Short + } else { + p.currentDirection = UnknownSide } if p.currentDirection.IsLong() { p.exposure = longSide.Sub(shortSide) @@ -309,6 +324,9 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { // CalculatePNL this is a localised generic way of calculating open // positions' worth func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { + if calc == nil { + return nil, ErrNilPNLCalculator + } result := &PNLResult{} var price, amount decimal.Decimal var err error @@ -337,6 +355,7 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) } return result, nil } else if calc.TimeBasedCalculation != nil { + result.Time = calc.TimeBasedCalculation.Time price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) diff := p.entryPrice.Sub(price) result.UnrealisedPNL = p.exposure.Mul(diff) @@ -363,25 +382,21 @@ func (p *PositionTracker) calculatePNLAndSwapSides(calc *PNLCalculator, value, p } func (p *PositionTracker) calculateUnrealisedPNL(side Side, amount, price decimal.Decimal) (decimal.Decimal, error) { - hello := amount.Mul(price) + cost := amount.Mul(price) var resp decimal.Decimal switch { case p.currentDirection.IsShort() && side.IsShort(), p.currentDirection.IsLong() && side.IsLong(): - resp = p.unrealisedPNL.Add(hello) + resp = p.unrealisedPNL.Add(cost) case p.currentDirection.IsShort() && side.IsLong(), p.currentDirection.IsLong() && side.IsShort(): - resp = p.unrealisedPNL.Sub(hello) + resp = p.unrealisedPNL.Sub(cost) default: - return resp, fmt.Errorf("%v %v %v %v whats wrong", p.currentDirection, side, resp, hello) + return resp, fmt.Errorf("%v %v %v %v %w", p.currentDirection, side, resp, cost, errCannotCalculateUnrealisedPNL) } return resp, nil } -func (p *PositionTracker) TrackPrice(price decimal.Decimal) decimal.Decimal { - return p.exposure.Mul(price) -} - // UpsertPNLEntry upserts an entry to PNLHistory field // with some basic checks func (p *PositionTracker) UpsertPNLEntry(entry PNLResult) error { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 0c5faa1297a..5e821b13825 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -31,23 +31,30 @@ func TestTrackPNL(t *testing.T) { t.Error(err) } fPNL := &FakePNL{ - result: &PNLResult{}, + result: &PNLResult{ + Time: time.Now(), + }, } - e := PositionTrackerController{ - exchange: exch, - exchangePNLCalculation: fPNL, + e := MultiPositionTracker{ + exchange: exch, + useExchangePNLCalculations: true, + exchangePNLCalculation: fPNL, } - setup := &PositionControllerSetup{} - f, err := e.SetupPositionTracker(item, pair, pair.Base) + setup := &PositionTrackerSetup{ + Pair: pair, + Asset: item, + } + + f, err := e.SetupPositionTracker(setup) if !errors.Is(err, nil) { t.Error(err) } - err = f.TrackPNLByTime(time.Now(), decimal.Zero, decimal.Zero) + err = f.TrackPNLByTime(time.Now(), 0) if !errors.Is(err, nil) { t.Error(err) } fPNL.err = errMissingPNLCalculationFunctions - err = f.TrackPNLByTime(time.Now(), decimal.Zero, decimal.Zero) + err = f.TrackPNLByTime(time.Now(), 0) if !errors.Is(err, errMissingPNLCalculationFunctions) { t.Error(err) } @@ -56,12 +63,12 @@ func TestTrackPNL(t *testing.T) { func TestUpsertPNLEntry(t *testing.T) { t.Parallel() f := &PositionTracker{} - err := f.UpsertPNLEntry(PNLHistory{}) + err := f.UpsertPNLEntry(PNLResult{}) if !errors.Is(err, errTimeUnset) { t.Error(err) } tt := time.Now() - err = f.UpsertPNLEntry(PNLHistory{Time: tt}) + err = f.UpsertPNLEntry(PNLResult{Time: tt}) if !errors.Is(err, nil) { t.Error(err) } @@ -69,7 +76,7 @@ func TestUpsertPNLEntry(t *testing.T) { t.Errorf("expected 1 received %v", len(f.pnlHistory)) } - err = f.UpsertPNLEntry(PNLHistory{Time: tt}) + err = f.UpsertPNLEntry(PNLResult{Time: tt}) if !errors.Is(err, nil) { t.Error(err) } @@ -86,11 +93,15 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - e := PositionTrackerController{ + e := MultiPositionTracker{ exchange: "test", exchangePNLCalculation: &FakePNL{}, } - f, err := e.SetupPositionTracker(item, pair, pair.Base) + setup := &PositionTrackerSetup{ + Pair: pair, + Asset: item, + } + f, err := e.SetupPositionTracker(setup) if !errors.Is(err, nil) { t.Error(err) } @@ -120,6 +131,12 @@ func TestTrackNewOrder(t *testing.T) { od.Amount = 1 od.ID = "2" err = f.TrackNewOrder(od) + if !errors.Is(err, errTimeUnset) { + t.Error(err) + } + + od.Date = time.Now() + err = f.TrackNewOrder(od) if !errors.Is(err, nil) { t.Error(err) } @@ -174,10 +191,10 @@ func TestTrackNewOrder(t *testing.T) { t.Error(err) } if f.currentDirection != UnknownSide { - t.Error("expected recognition that its unknown") + t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) } if f.status != Closed { - t.Error("expected closed position") + t.Errorf("expected recognition that its closed, received '%v'", f.status) } err = f.TrackNewOrder(od) @@ -185,51 +202,57 @@ func TestTrackNewOrder(t *testing.T) { t.Error(err) } if f.currentDirection != UnknownSide { - t.Error("expected recognition that its unknown") + t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) } if f.status != Closed { - t.Error("expected closed position") + t.Errorf("expected recognition that its closed, received '%v'", f.status) } } func TestSetupFuturesTracker(t *testing.T) { t.Parallel() - _, err := SetupPositionTrackerController(nil) + _, err := SetupMultiPositionTracker(nil) if !errors.Is(err, errNilSetup) { t.Error(err) } setup := &PositionControllerSetup{} - _, err = SetupPositionTrackerController(setup) + _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, errExchangeNameEmpty) { t.Error(err) } setup.Exchange = "test" - _, err = SetupPositionTrackerController(setup) + _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, errNotFutureAsset) { t.Error(err) } setup.Asset = asset.Futures - _, err = SetupPositionTrackerController(setup) + _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, ErrPairIsEmpty) { t.Error(err) } setup.Pair = currency.NewPair(currency.BTC, currency.USDT) - _, err = SetupPositionTrackerController(setup) + _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, errEmptyUnderlying) { t.Error(err) } setup.Underlying = currency.BTC - _, err = SetupPositionTrackerController(setup) + _, err = SetupMultiPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + + setup.UseExchangePNLCalculation = true + _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, errMissingPNLCalculationFunctions) { t.Error(err) } setup.ExchangePNLCalculation = &FakePNL{} - resp, err := SetupPositionTrackerController(setup) + resp, err := SetupMultiPositionTracker(setup) if !errors.Is(err, nil) { t.Error(err) } @@ -250,12 +273,15 @@ func TestExchangeTrackNewOrder(t *testing.T) { Underlying: pair.Base, ExchangePNLCalculation: &FakePNL{}, } - resp, err := SetupPositionTrackerController(setup) + resp, err := SetupMultiPositionTracker(setup) if !errors.Is(err, nil) { t.Error(err) } + tt := time.Now() + err = resp.TrackNewOrder(&Detail{ + Date: tt, Exchange: exch, AssetType: item, Pair: pair, @@ -271,6 +297,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { } err = resp.TrackNewOrder(&Detail{ + Date: tt, Exchange: exch, AssetType: item, Pair: pair, @@ -286,6 +313,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { } err = resp.TrackNewOrder(&Detail{ + Date: tt, Exchange: exch, AssetType: item, Pair: pair, @@ -305,6 +333,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { resp.positions[0].status = Open resp.positions = append(resp.positions, resp.positions...) err = resp.TrackNewOrder(&Detail{ + Date: tt, Exchange: exch, AssetType: item, Pair: pair, @@ -312,7 +341,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { ID: "2", Amount: 2, }) - if !errors.Is(err, errPositionDiscrepancy) { + if !errors.Is(err, errCannotCalculateUnrealisedPNL) { t.Error(err) } if len(resp.positions) != 2 { @@ -321,6 +350,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { resp.positions[0].status = Closed err = resp.TrackNewOrder(&Detail{ + Date: tt, Exchange: exch, Pair: pair, AssetType: asset.USDTMarginedFutures, @@ -331,5 +361,151 @@ func TestExchangeTrackNewOrder(t *testing.T) { if !errors.Is(err, errAssetMismatch) { t.Error(err) } +} + +func TestSetupPositionControllerReal(t *testing.T) { + t.Parallel() + pc := SetupPositionController() + if pc.positionTrackerControllers == nil { + t.Error("unexpected nil") + } +} + +func TestPositionControllerTestTrackNewOrder(t *testing.T) { + t.Parallel() + pc := SetupPositionController() + err := pc.TrackNewOrder(nil) + if !errors.Is(err, errNilOrder) { + t.Error(err) + } + + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Spot, + Side: Long, + ID: "lol", + }) + if !errors.Is(err, errNotFutureAsset) { + t.Error(err) + } + + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + Side: Long, + ID: "lol", + }) + if !errors.Is(err, nil) { + t.Error(err) + } +} + +func TestGetRealisedPNL(t *testing.T) { + t.Parallel() + pt := PositionTracker{} + _, err := pt.GetRealisedPNL() + if !errors.Is(err, errPositionNotClosed) { + t.Error(err) + } + pt.status = Closed + pt.realisedPNL = decimal.NewFromInt(1337) + result, err := pt.GetRealisedPNL() + if !errors.Is(err, nil) { + t.Error(err) + } + if !pt.realisedPNL.Equal(result) { + t.Errorf("expected 1337, received '%v'", result) + } +} + +func TestGetLatestPNLSnapshot(t *testing.T) { + t.Parallel() + pt := PositionTracker{} + _, err := pt.GetLatestPNLSnapshot() + if !errors.Is(err, errNoPNLHistory) { + t.Error(err) + } + + pnl := PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNL: decimal.NewFromInt(1337), + } + pt.pnlHistory = append(pt.pnlHistory, pnl) + + result, err := pt.GetLatestPNLSnapshot() + if !errors.Is(err, nil) { + t.Error(err) + } + if result != pt.pnlHistory[0] { + t.Error("unexpected result") + } +} + +func TestCalculatePNL(t *testing.T) { + t.Parallel() + pt := PositionTracker{} + _, err := pt.CalculatePNL(nil) + if !errors.Is(err, ErrNilPNLCalculator) { + t.Error(err) + } + + _, err = pt.CalculatePNL(&PNLCalculator{}) + if !errors.Is(err, errMissingPNLCalculationFunctions) { + t.Error(err) + } + tt := time.Now() + result, err := pt.CalculatePNL(&PNLCalculator{ + TimeBasedCalculation: &TimeBasedCalculation{ + Time: tt, + CurrentPrice: 1337, + }, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if !result.Time.Equal(tt) { + t.Error("unexpected result") + } + + pt.status = Open + pt.currentDirection = Long + result, err = pt.CalculatePNL(&PNLCalculator{ + OrderBasedCalculation: &Detail{ + Date: tt, + Price: 1337, + Exchange: "test", + AssetType: asset.Spot, + Side: Long, + Status: Active, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Amount: 5, + }, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + + pt.exposure = decimal.NewFromInt(5) + result, err = pt.CalculatePNL(&PNLCalculator{ + OrderBasedCalculation: &Detail{ + Date: tt, + Price: 1337, + Exchange: "test", + AssetType: asset.Spot, + Side: Short, + Status: Active, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Amount: 10, + }, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + // todo do a proper test of values to ensure correct split calculation! } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index b5e1d1b9dc0..1151a2c7daa 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -15,12 +15,17 @@ var ( errTimeUnset = errors.New("time unset") errMissingPNLCalculationFunctions = errors.New("futures tracker requires exchange PNL calculation functions") errOrderNotEqualToTracker = errors.New("order does not match tracker data") - errPositionClosed = errors.New("the position is closed, time for a new one") + errPositionClosed = errors.New("the position is closed") + errPositionNotClosed = errors.New("the position is not closed") errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") errAssetMismatch = errors.New("provided asset does not match") errEmptyUnderlying = errors.New("underlying asset unset") errNoPositions = errors.New("there are no positions") errNilSetup = errors.New("nil setup received") + errNilOrder = errors.New("nil order received") + errNoPNLHistory = errors.New("no pnl history") + errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL, order is not open") + // ErrNilPNLCalculator is raised when pnl calculation is requested for // an exchange, but the fields are not set properly ErrNilPNLCalculator = errors.New("nil pnl calculator received") @@ -44,14 +49,14 @@ type CollateralManagement interface { } type PositionController struct { - positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*PositionTrackerController + positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker } -// PositionTrackerController will track the performance of +// MultiPositionTracker will track the performance of // futures positions over time. If an old position tracker // is closed, then the position controller will create a new one // to track the current positions -type PositionTrackerController struct { +type MultiPositionTracker struct { exchange string asset asset.Item pair currency.Pair From 88b12e16ede3e9a991bebd2bb314950bed8af22f Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 20 Dec 2021 17:17:32 +1100 Subject: [PATCH 043/171] labels things add scaling collateral test --- exchanges/ftx/ftx_test.go | 39 ++++++++++++++++++++++++++++++++ exchanges/ftx/ftx_wrapper.go | 34 ++++++++++------------------ exchanges/order/futures.go | 10 ++++++++ exchanges/order/futures_types.go | 8 ++++++- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 137833e740b..5e687d6fc0e 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1824,3 +1824,42 @@ func TestCalculatePNLFromOrders(t *testing.T) { t.Errorf("expected nil err, received '%v', expected -0.0029, received '%v'", err, pnl) } } + +func TestScaleCollateral(t *testing.T) { + ai, err := f.GetAccountInfo(context.Background()) + if err != nil { + t.Error(err) + } + t.Log(ai.Collateral) + walletInfo, err := f.GetAllWalletBalances(context.Background()) + if err != nil { + t.Error(err) + } + t.Log(walletInfo) + myCollat := 0.0 + for _, v := range walletInfo { + for v2 := range v { + coin := v[v2].Coin + if coin == "USD" { + myCollat += v[v2].Total + continue + } + tick, err := f.FetchTicker(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "-"), asset.Spot) + if err != nil { + t.Error(err) + } + scaled, err := f.ScaleCollateral(&order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Last), + }) + if err != nil { + t.Error(err) + } + myCollat += scaled.InexactFloat64() + } + } + t.Log(myCollat) +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index d86dc4ccf0f..6803359d4d6 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1273,17 +1273,15 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur return availableChains, nil } -func (f *FTX) CalculateUnrealisedPNL(positionSize, markPrice, prevMarkPrice float64) float64 { - return positionSize * (markPrice - prevMarkPrice) -} - +// CalculatePNL uses a high-tech algorithm to calculate your pnl func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { if pnl.ExchangeBasedCalculation == nil { return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } var result order.PNLResult if pnl.ExchangeBasedCalculation.CalculateOffline { - uPNL := f.CalculateUnrealisedPNL(pnl.ExchangeBasedCalculation.Amount, pnl.ExchangeBasedCalculation.CurrentPrice, pnl.ExchangeBasedCalculation.PreviousPrice) + uPNL := pnl.ExchangeBasedCalculation.Amount * (pnl.ExchangeBasedCalculation.CurrentPrice - pnl.ExchangeBasedCalculation.PreviousPrice) + result.RealisedPNL = result.RealisedPNL.Add(result.UnrealisedPNL) result.UnrealisedPNL = decimal.NewFromFloat(uPNL) return &result, nil } @@ -1315,6 +1313,7 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { return &result, nil } +// ScaleCollateral takes your totals and scales them according to FTX's rules func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] @@ -1325,33 +1324,24 @@ func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal if collateralWeight.IMFFactor == 0 { return decimal.Zero, errCoinMustBeSpecified } - what := decimal.NewFromFloat(collateralWeight.Total) - what2 := decimal.NewFromFloat(1.1 / collateralWeight.IMFFactor * math.Sqrt(calc.CollateralAmount.InexactFloat64())) - result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(what, what2))) + total := decimal.NewFromFloat(collateralWeight.Total) + weight := decimal.NewFromFloat(1.1 / collateralWeight.IMFFactor * math.Sqrt(calc.CollateralAmount.InexactFloat64())) + result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(total, weight))) } else { result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) } return result, nil } +// CalculateTotalCollateral scales collateral and determines how much you have in USD (maybe) func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal for i := range collateralAssets { - collateralWeight, ok := f.collateralWeight[collateralAssets[i].CollateralCurrency.Upper().String()] - if !ok { - return decimal.Zero, errCoinMustBeSpecified - } - if collateralAssets[i].CollateralAmount.IsPositive() { - if collateralWeight.IMFFactor == 0 { - return decimal.Zero, errCoinMustBeSpecified - } - what := decimal.NewFromFloat(collateralWeight.Total) - what2 := decimal.NewFromFloat(1.1 / collateralWeight.IMFFactor * math.Sqrt(collateralAssets[i].CollateralAmount.InexactFloat64())) - result = result.Add(collateralAssets[i].CollateralAmount.Mul(collateralAssets[i].USDPrice).Mul(decimal.Min(what, what2))) - - } else { - result = result.Add(collateralAssets[i].CollateralAmount.Mul(collateralAssets[i].USDPrice)) + collateral, err := f.ScaleCollateral(&collateralAssets[i]) + if err != nil { + return decimal.Zero, err } + result = result.Add(collateral) } return result, nil } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 4a7402617e6..0abaf705114 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -11,12 +11,17 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) +// SetupPositionController creates a position controller +// to track futures orders func SetupPositionController() *PositionController { return &PositionController{ positionTrackerControllers: make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker), } } +// TrackNewOrder sets up the maps to then creaste a +// multi position tracker which funnels down into the +// position tracker, to then track an order's pnl func (c *PositionController) TrackNewOrder(d *Detail) error { if d == nil { return errNilOrder @@ -77,6 +82,7 @@ func SetupMultiPositionTracker(setup *PositionControllerSetup) (*MultiPositionTr }, nil } +// GetPositions returns all positions func (e *MultiPositionTracker) GetPositions() []*PositionTracker { return e.positions } @@ -187,6 +193,8 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro }) } +// GetRealisedPNL returns the realised pnl if the order +// is closed func (p *PositionTracker) GetRealisedPNL() (decimal.Decimal, error) { if p.status != Closed { return decimal.Zero, fmt.Errorf("%v %v %v %w", p.exchange, p.asset, p.contractPair, errPositionNotClosed) @@ -194,6 +202,8 @@ func (p *PositionTracker) GetRealisedPNL() (decimal.Decimal, error) { return p.realisedPNL, nil } +// GetLatestPNLSnapshot takes the latest pnl history value +// and returns it func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { if len(p.pnlHistory) == 0 { return PNLResult{}, fmt.Errorf("%v %v %v %w", p.exchange, p.asset, p.contractPair, errNoPNLHistory) diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 1151a2c7daa..00b48f574a5 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -20,7 +20,6 @@ var ( errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") errAssetMismatch = errors.New("provided asset does not match") errEmptyUnderlying = errors.New("underlying asset unset") - errNoPositions = errors.New("there are no positions") errNilSetup = errors.New("nil setup received") errNilOrder = errors.New("nil order received") errNoPNLHistory = errors.New("no pnl history") @@ -48,6 +47,11 @@ type CollateralManagement interface { CalculateTotalCollateral([]CollateralCalculator) (decimal.Decimal, error) } +// PositionController manages all futures orders +// across all exchanges assets and pairs +// its purpose is to handle the minutia of tracking +// and so all you need to do is send all orders to +// the position controller and its all tracked happily type PositionController struct { positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker } @@ -111,6 +115,8 @@ type PositionTracker struct { latestPrice decimal.Decimal } +// PositionTrackerSetup contains all required fields to +// setup a position tracker type PositionTrackerSetup struct { Pair currency.Pair EntryPrice float64 From 3872ea660d5edecc3f3e6e33c29cc591d7692f15 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 22 Dec 2021 15:45:07 +1100 Subject: [PATCH 044/171] Calculates pnl in line with fees --- .../eventhandlers/portfolio/portfolio.go | 2 +- exchanges/ftx/ftx_test.go | 153 +++++++++++++++++- exchanges/ftx/ftx_types.go | 13 +- exchanges/ftx/ftx_wrapper.go | 70 ++++---- exchanges/interfaces.go | 2 +- exchanges/order/futures.go | 147 ++++++++++++----- exchanges/order/futures_types.go | 33 ++-- 7 files changed, 332 insertions(+), 88 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 9b57161874e..42c7cb3a4de 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -496,7 +496,7 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch g return nil } - var pnl gctorder.PNLManagement + var pnl gctorder.PNLCalculation if settings.UseExchangePNLCalculation { pnl = exch } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 5e687d6fc0e..f9cb96b84d9 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1826,25 +1826,28 @@ func TestCalculatePNLFromOrders(t *testing.T) { } func TestScaleCollateral(t *testing.T) { + f.Verbose = true ai, err := f.GetAccountInfo(context.Background()) if err != nil { t.Error(err) } - t.Log(ai.Collateral) walletInfo, err := f.GetAllWalletBalances(context.Background()) if err != nil { t.Error(err) } - t.Log(walletInfo) myCollat := 0.0 + liquidationVersion := 0.0 + usdHo := 0.0 for _, v := range walletInfo { for v2 := range v { coin := v[v2].Coin if coin == "USD" { myCollat += v[v2].Total + usdHo += v[v2].USDValue + liquidationVersion += v[v2].Total continue } - tick, err := f.FetchTicker(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "-"), asset.Spot) + tick, err := f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) if err != nil { t.Error(err) } @@ -1853,13 +1856,153 @@ func TestScaleCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.NewFromFloat(tick.Last), + USDPrice: decimal.NewFromFloat(tick.Price), }) if err != nil { t.Error(err) } myCollat += scaled.InexactFloat64() + usdHo += v[v2].USDValue + + scaled, err = f.ScaleCollateral(&order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + IsLiquidating: true, + }) + if err != nil { + t.Error(err) + } + + liquidationVersion += scaled.InexactFloat64() + } + } + t.Logf("%v collateral", ai.Collateral) + t.Logf("%v my calcs", myCollat) + t.Logf("%v liquidation calcs", liquidationVersion) + t.Logf("%v usd total", usdHo) +} + +func TestCalculatePNLFromOrders1(t *testing.T) { + f.Verbose = true + resp, err := f.GetFills(context.Background(), "BTC-1231", "200", time.Time{}, time.Time{}) + result := resp + sort.Slice(result, func(i, j int) bool { + return result[i].Time.Before(result[j].Time) + }) + + pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) + var orders []order.Detail + for i := range result { + price := result[i].Price + side, err := order.StringToOrderSide(result[i].Side) + if err != nil { + t.Error(err) + } + orders = append(orders, order.Detail{ + Side: side, + Pair: pair, + ID: fmt.Sprintf("%v", result[i].ID), + Price: price, + Amount: result[i].Size, + AssetType: asset.Futures, + Exchange: f.Name, + Fee: result[i].Fee, + Date: result[i].Time, + }) + } + + exch := f.Name + item := asset.Futures + setup := &order.PositionControllerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + //ExchangePNLCalculation: &f, + //UseExchangePNLCalculation: true, + //OfflineCalculation: true, + } + p, err := order.SetupMultiPositionTracker(setup) + if err != nil { + t.Error(err) + } + for i := range orders { + err = p.TrackNewOrder(&orders[i]) + if err != nil { + t.Error(err) + } + } + positions := p.GetPositions() + for i := range positions { + t.Log(positions[i].GetLatestPNLSnapshot()) + } +} + +func TestCalculatePNLFromOrders3(t *testing.T) { + pressXToJSON := `{"success":true,"result":[ + {"id":102334270248,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49068.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:41.551614+00:00","future":"BTC-1231"}, + {"id":102334231437,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0004,"status":"closed","filledSize":0.0004,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49050.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:27.615861+00:00","future":"BTC-1231"}, + {"id":102333965786,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0003,"status":"closed","filledSize":0.0003,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49030.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:19:04.483189+00:00","future":"BTC-1231"}, + {"id":102333762284,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49061.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:07.703362+00:00","future":"BTC-1231"}, + {"id":102333732631,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49063.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:02.044371+00:00","future":"BTC-1231"} +],"hasMoreData":false}` + resp := struct { + Data []OrderData `json:"result"` + }{} + err := json.Unmarshal([]byte(pressXToJSON), &resp) + if err != nil { + t.Fatal() + } + result := resp.Data + sort.Slice(result, func(i, j int) bool { + return result[i].CreatedAt.Before(result[j].CreatedAt) + }) + + pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) + var orders []order.Detail + for i := range result { + price := result[i].AvgFillPrice + side, err := order.StringToOrderSide(result[i].Side) + if err != nil { + t.Error(err) + } + orders = append(orders, order.Detail{ + Side: side, + Pair: pair, + ID: fmt.Sprintf("%v", result[i].ID), + Price: price, + Amount: result[i].Size, + Status: order.Status(result[i].Status), + AssetType: asset.Futures, + Exchange: f.Name, + Date: result[i].CreatedAt, + }) + } + + exch := f.Name + item := asset.Futures + setup := &order.PositionControllerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: pair.Base, + ExchangePNLCalculation: &f, + UseExchangePNLCalculation: false, + } + p, err := order.SetupMultiPositionTracker(setup) + if err != nil { + t.Error(err) + } + for i := range orders { + err = p.TrackNewOrder(&orders[i]) + if err != nil { + t.Error(err) } } - t.Log(myCollat) + pos := p.GetPositions() + pnl, _ := pos[0].GetRealisedPNL() + t.Logf("%v", pnl) } diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index 1095a274ca0..9ebac31f5ab 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -184,21 +184,26 @@ type IndexWeights struct { // PositionData stores data of an open position type PositionData struct { + CollateralUsed float64 `json:"collateralUsed"` Cost float64 `json:"cost"` + CumulativeBuySize float64 `json:"cumulativeBuySize"` + CumulativeSellSize float64 `json:"cumulativeSellSize"` EntryPrice float64 `json:"entryPrice"` + EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"` Future string `json:"future"` InitialMarginRequirement float64 `json:"initialMarginRequirement"` LongOrderSize float64 `json:"longOrderSize"` MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"` NetSize float64 `json:"netSize"` OpenSize float64 `json:"openSize"` - RealizedPnL float64 `json:"realizedPnL"` + RealizedPNL float64 `json:"realizedPnl"` + RecentAverageOpenPrice float64 `json:"recentAverageOpenPrice"` + RecentBreakEvenPrice float64 `json:"recentBreakEvenPrice"` + RecentPnl float64 `json:"recentPnl"` ShortOrderSize float64 `json:"shortOrderSize"` Side string `json:"side"` Size float64 `json:"size"` - UnrealizedPnL float64 `json:"unrealizedPnL"` - CollateralUsed float64 `json:"collateralUsed"` - EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"` + UnrealizedPNL float64 `json:"unrealizedPnl"` } // AccountInfoData stores account data diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 6803359d4d6..8b61daf80dc 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1273,43 +1273,48 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur return availableChains, nil } +// entry price +// side +// current price +// previous price +// amount + // CalculatePNL uses a high-tech algorithm to calculate your pnl func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { if pnl.ExchangeBasedCalculation == nil { return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } var result order.PNLResult - if pnl.ExchangeBasedCalculation.CalculateOffline { - uPNL := pnl.ExchangeBasedCalculation.Amount * (pnl.ExchangeBasedCalculation.CurrentPrice - pnl.ExchangeBasedCalculation.PreviousPrice) - result.RealisedPNL = result.RealisedPNL.Add(result.UnrealisedPNL) - result.UnrealisedPNL = decimal.NewFromFloat(uPNL) - return &result, nil - } + result.Time = pnl.ExchangeBasedCalculation.Time - info, err := f.GetAccountInfo(context.Background()) - if err != nil { - return nil, err - } - if info.Liquidating || info.Collateral == 0 { - return &result, order.ErrPositionLiquidated - } - for i := range info.Positions { - ftxSide := order.Side(info.Positions[i].Side) - var pnlSide order.Side - switch { - case ftxSide.IsShort(): - pnlSide = order.Short - case ftxSide.IsLong(): - pnlSide = order.Long - default: - return nil, order.ErrSideIsInvalid + if !pnl.ExchangeBasedCalculation.CalculateOffline { + info, err := f.GetAccountInfo(context.Background()) + if err != nil { + return nil, err } - if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice && pnl.ExchangeBasedCalculation.Side == pnlSide { - result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPnL) - result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPnL) - return &result, nil + if info.Liquidating || info.Collateral == 0 { + return &result, order.ErrPositionLiquidated + } + for i := range info.Positions { + pair, err := currency.NewPairFromString(info.Positions[i].Future) + if err != nil { + return nil, err + } + if !pnl.ExchangeBasedCalculation.Pair.Equal(pair) { + continue + } + if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice { + result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) + result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPNL) + result.Price = decimal.NewFromFloat(info.Positions[i].Cost) + return &result, nil + } } } + uPNL := pnl.ExchangeBasedCalculation.Amount * (pnl.ExchangeBasedCalculation.CurrentPrice - pnl.ExchangeBasedCalculation.PreviousPrice) + result.RealisedPNL = result.RealisedPNL.Add(result.UnrealisedPNL) + result.UnrealisedPNL = decimal.NewFromFloat(uPNL) + result.Price = decimal.NewFromFloat(pnl.ExchangeBasedCalculation.CurrentPrice) return &result, nil } @@ -1324,9 +1329,14 @@ func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal if collateralWeight.IMFFactor == 0 { return decimal.Zero, errCoinMustBeSpecified } - total := decimal.NewFromFloat(collateralWeight.Total) - weight := decimal.NewFromFloat(1.1 / collateralWeight.IMFFactor * math.Sqrt(calc.CollateralAmount.InexactFloat64())) - result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(total, weight))) + var scaling decimal.Decimal + if calc.IsLiquidating { + scaling = decimal.NewFromFloat(collateralWeight.Total) + } else { + scaling = decimal.NewFromFloat(collateralWeight.Initial) + } + weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.IMFFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) + result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) } else { result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) } diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index 30d26f52646..bab9d09cb8c 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -82,7 +82,7 @@ type IBotExchange interface { EnableRateLimiter() error CurrencyStateManagement - order.PNLManagement + order.PNLCalculation order.CollateralManagement GetWebsocket() (*stream.Websocket, error) diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 0abaf705114..b86653b92be 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -9,6 +9,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -117,11 +118,12 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { } } setup := &PositionTrackerSetup{ - Pair: d.Pair, - EntryPrice: d.Price, - Underlying: d.Pair.Base, - Asset: d.AssetType, - Side: d.Side, + Pair: d.Pair, + EntryPrice: d.Price, + Underlying: d.Pair.Base, + Asset: d.AssetType, + Side: d.Side, + UseExchangePNLCalculation: e.useExchangePNLCalculations, } tracker, err := e.SetupPositionTracker(setup) if err != nil { @@ -154,19 +156,21 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) } resp := &PositionTracker{ - exchange: strings.ToLower(e.exchange), - asset: setup.Asset, - contractPair: setup.Pair, - underlyingAsset: setup.Underlying, - status: Open, - entryPrice: decimal.NewFromFloat(setup.EntryPrice), - currentDirection: setup.Side, + exchange: strings.ToLower(e.exchange), + asset: setup.Asset, + contractPair: setup.Pair, + underlyingAsset: setup.Underlying, + status: Open, + entryPrice: decimal.NewFromFloat(setup.EntryPrice), + currentDirection: setup.Side, + openingDirection: setup.Side, + useExchangePNLCalculation: e.useExchangePNLCalculations, } if !e.useExchangePNLCalculations { // use position tracker's pnl calculation by default - resp.PNLManagement = resp + resp.PNLCalculation = resp } else { - resp.PNLManagement = e.exchangePNLCalculation + resp.PNLCalculation = e.exchangePNLCalculation } return resp, nil } @@ -177,7 +181,7 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro defer func() { p.latestPrice = decimal.NewFromFloat(currentPrice) }() - pnl, err := p.PNLManagement.CalculatePNL(&PNLCalculator{ + pnl, err := p.PNLCalculation.CalculatePNL(&PNLCalculator{ TimeBasedCalculation: &TimeBasedCalculation{ Time: t, CurrentPrice: currentPrice, @@ -244,7 +248,6 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { for i := range p.shortPositions { if p.shortPositions[i].ID == d.ID { - // update, not overwrite ord := p.shortPositions[i].Copy() ord.UpdateOrderFromDetail(d) p.shortPositions[i] = ord @@ -281,7 +284,27 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.currentDirection = d.Side } - result, err := p.PNLManagement.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) + var result *PNLResult + var err error + if p.useExchangePNLCalculation { + cal := &ExchangeBasedCalculation{ + Underlying: p.underlyingAsset, + Asset: p.asset, + Side: d.Side, + Leverage: d.Leverage, + EntryPrice: p.entryPrice.InexactFloat64(), + Amount: d.Amount, + CurrentPrice: d.Price, + Pair: p.contractPair, + Time: d.Date, + } + if len(p.pnlHistory) != 0 { + cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price.InexactFloat64() + } + result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{ExchangeBasedCalculation: cal}) + } else { + result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) + } if err != nil { if errors.Is(err, ErrPositionLiquidated) { err = p.UpsertPNLEntry(*result) @@ -302,6 +325,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return err } p.unrealisedPNL = result.UnrealisedPNL + p.realisedPNL = result.RealisedPNL if longSide.GreaterThan(shortSide) { p.currentDirection = Long @@ -318,8 +342,6 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) - p.realisedPNL = p.unrealisedPNL - p.unrealisedPNL = decimal.Zero } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short @@ -341,34 +363,38 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) var price, amount decimal.Decimal var err error if calc.OrderBasedCalculation != nil { - result.Time = calc.OrderBasedCalculation.Date price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) if (p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && p.exposure.LessThan(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)) { - one := p.exposure.Sub(amount).Abs() - result.UnrealisedPNL, err = p.calculatePNLAndSwapSides(calc, one, price) + // this needs reworking to handle the exposure part of things now that I've done otherwise! + one := p.exposure.Sub(amount) + result, err = p.calculatePNLAndSwapSides(calc, one, price) if err != nil { return result, err } - two := amount.Sub(p.exposure).Abs() - result.UnrealisedPNL, err = p.calculatePNLAndSwapSides(calc, two, price) + two := amount.Sub(p.exposure) + result, err = p.calculatePNLAndSwapSides(calc, two, price) if err != nil { return result, err } + result.UnrealisedPNL = result.UnrealisedPNL.Sub(decimal.NewFromFloat(calc.OrderBasedCalculation.Fee)) + result.RealisedPNL = result.RealisedPNL.Sub(decimal.NewFromFloat(calc.OrderBasedCalculation.Fee)) return result, nil } - result.UnrealisedPNL, err = p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Side, amount, price) + result, err = p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, amount, price, decimal.NewFromFloat(calc.OrderBasedCalculation.Fee)) if err != nil { return nil, err } + return result, nil } else if calc.TimeBasedCalculation != nil { result.Time = calc.TimeBasedCalculation.Time price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) - diff := p.entryPrice.Sub(price) + diff := price.Sub(p.entryPrice) result.UnrealisedPNL = p.exposure.Mul(diff) + result.Price = price return result, nil } @@ -377,12 +403,11 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) // calculatePNLAndSwapSides is used to help calculate PNL when // a new position flips the direction without closing the overall position -func (p *PositionTracker) calculatePNLAndSwapSides(calc *PNLCalculator, value, price decimal.Decimal) (decimal.Decimal, error) { - result, err := p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Side, value, price) +func (p *PositionTracker) calculatePNLAndSwapSides(calc *PNLCalculator, value, price decimal.Decimal) (*PNLResult, error) { + result, err := p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, value, price, decimal.Zero) if err != nil { - return result, err + return nil, err } - p.unrealisedPNL = result if calc.OrderBasedCalculation.Side.IsShort() { calc.OrderBasedCalculation.Side = Long } else if calc.OrderBasedCalculation.Side.IsLong() { @@ -391,20 +416,70 @@ func (p *PositionTracker) calculatePNLAndSwapSides(calc *PNLCalculator, value, p return result, nil } -func (p *PositionTracker) calculateUnrealisedPNL(side Side, amount, price decimal.Decimal) (decimal.Decimal, error) { +/// IT SEEMS LIKE ITS CURRENT EXPOSURE, TIMES THE PRICE +func (p *PositionTracker) calculateUnrealisedPNL(t time.Time, side Side, amount, price, fee decimal.Decimal) (*PNLResult, error) { + var previousPNL *PNLResult + if len(p.pnlHistory) > 0 { + previousPNL = &p.pnlHistory[len(p.pnlHistory)-1] + } cost := amount.Mul(price) - var resp decimal.Decimal + var prevExposure, prevRealisedPNL decimal.Decimal + if previousPNL != nil { + prevRealisedPNL = previousPNL.RealisedPNL + prevExposure = previousPNL.Exposure + } + var currentExposure, realisedPNL, unrealisedPNL, first, second decimal.Decimal + if p.openingDirection.IsLong() { + first = price + if previousPNL != nil { + second = p.entryPrice + } + } else if p.openingDirection.IsShort() { + first = p.entryPrice + if previousPNL != nil { + second = price + } + } + hi := price.InexactFloat64() + log.Debugf(log.RequestSys, "%v", hi) switch { case p.currentDirection.IsShort() && side.IsShort(), p.currentDirection.IsLong() && side.IsLong(): - resp = p.unrealisedPNL.Add(cost) + // appending to your position + currentExposure = prevExposure.Add(amount) + unrealisedPNL = currentExposure.Mul(first.Sub(second)) case p.currentDirection.IsShort() && side.IsLong(), p.currentDirection.IsLong() && side.IsShort(): - resp = p.unrealisedPNL.Sub(cost) + // selling/closing your position by "amount" + currentExposure = prevExposure.Sub(amount) + unrealisedPNL = currentExposure.Mul(first.Sub(second)) + step1 := first.Sub(second) + step2 := amount.Mul(step1) + realisedPNL = prevRealisedPNL.Add(step2) default: - return resp, fmt.Errorf("%v %v %v %v %w", p.currentDirection, side, resp, cost, errCannotCalculateUnrealisedPNL) + return nil, fmt.Errorf("%v %v %v %v %w", p.currentDirection, side, currentExposure, cost, errCannotCalculateUnrealisedPNL) } - return resp, nil + totalFees := fee + for i := range p.pnlHistory { + totalFees = totalFees.Add(p.pnlHistory[i].Fee) + } + + if !unrealisedPNL.IsZero() { + unrealisedPNL = unrealisedPNL.Sub(totalFees) + } + if !realisedPNL.IsZero() { + realisedPNL = realisedPNL.Sub(totalFees) + } + + response := &PNLResult{ + Time: t, + UnrealisedPNL: unrealisedPNL, + RealisedPNL: realisedPNL, + Price: price, + Exposure: currentExposure, + Fee: fee, + } + return response, nil } // UpsertPNLEntry upserts an entry to PNLHistory field diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 00b48f574a5..382bc99cff4 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -33,9 +33,9 @@ var ( ErrPositionLiquidated = errors.New("position liquidated") ) -// PNLManagement is an interface to allow multiple +// PNLCalculation is an interface to allow multiple // ways of calculating PNL to be used for futures positions -type PNLManagement interface { +type PNLCalculation interface { CalculatePNL(*PNLCalculator) (*PNLResult, error) } @@ -72,7 +72,7 @@ type MultiPositionTracker struct { pnl decimal.Decimal offlinePNLCalculation bool useExchangePNLCalculations bool - exchangePNLCalculation PNLManagement + exchangePNLCalculation PNLCalculation } // PositionControllerSetup holds the parameters @@ -84,7 +84,7 @@ type PositionControllerSetup struct { Underlying currency.Code OfflineCalculation bool UseExchangePNLCalculation bool - ExchangePNLCalculation PNLManagement + ExchangePNLCalculation PNLCalculation } // PositionTracker tracks futures orders until the overall position is considered closed @@ -101,6 +101,7 @@ type PositionTracker struct { underlyingAsset currency.Code exposure decimal.Decimal currentDirection Side + openingDirection Side status Status averageLeverage decimal.Decimal unrealisedPNL decimal.Decimal @@ -111,18 +112,20 @@ type PositionTracker struct { entryPrice decimal.Decimal closingPrice decimal.Decimal offlinePNLCalculation bool - PNLManagement - latestPrice decimal.Decimal + PNLCalculation + latestPrice decimal.Decimal + useExchangePNLCalculation bool } // PositionTrackerSetup contains all required fields to // setup a position tracker type PositionTrackerSetup struct { - Pair currency.Pair - EntryPrice float64 - Underlying currency.Code - Asset asset.Item - Side Side + Pair currency.Pair + EntryPrice float64 + Underlying currency.Code + Asset asset.Item + Side Side + UseExchangePNLCalculation bool } // CollateralCalculator is used to determine @@ -135,6 +138,7 @@ type CollateralCalculator struct { Side Side CollateralAmount decimal.Decimal USDPrice decimal.Decimal + IsLiquidating bool } // PNLCalculator is used to calculate PNL values @@ -157,6 +161,7 @@ type TimeBasedCalculation struct { // eg FTX uses a different method than Binance to calculate PNL // values type ExchangeBasedCalculation struct { + Pair currency.Pair CalculateOffline bool Underlying currency.Code Asset asset.Item @@ -167,6 +172,9 @@ type ExchangeBasedCalculation struct { Amount float64 CurrentPrice float64 PreviousPrice float64 + Time time.Time + OrderID string + Fee decimal.Decimal } // PNLResult stores pnl history at a point in time @@ -174,4 +182,7 @@ type PNLResult struct { Time time.Time UnrealisedPNL decimal.Decimal RealisedPNL decimal.Decimal + Price decimal.Decimal + Exposure decimal.Decimal + Fee decimal.Decimal } From f848b2fc52362391c44e836e7bc9dbc16493fb6c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 23 Dec 2021 12:52:47 +1100 Subject: [PATCH 045/171] Mostly accurate PNL, with exception to appending with diff prices --- exchanges/ftx/ftx_test.go | 17 ++-- exchanges/ftx/ftx_wrapper.go | 4 +- exchanges/order/futures.go | 150 +++++++++++++++---------------- exchanges/order/futures_test.go | 6 +- exchanges/order/futures_types.go | 12 +-- 5 files changed, 92 insertions(+), 97 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index f9cb96b84d9..b3f29bbe182 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1805,22 +1805,22 @@ func TestCalculatePNLFromOrders(t *testing.T) { if len(pos) != 6 { t.Fatal("expected 6 positions") } - if pnl, err := pos[0].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.1223)) || err != nil { + if pnl := pos[0].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.1223)) { t.Errorf("expected nil err, received '%v', expected 0.1223, received '%v'", err, pnl) } - if pnl, err := pos[1].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0148)) || err != nil { + if pnl := pos[1].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0148)) { t.Errorf("expected nil err, received '%v', expected 0.0148, received '%v'", err, pnl) } - if pnl, err := pos[2].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0092)) || err != nil { + if pnl := pos[2].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0092)) { t.Errorf("expected nil err, received '%v', expected 0.0092, received '%v'", err, pnl) } - if pnl, err := pos[3].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0054)) || err != nil { + if pnl := pos[3].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0054)) { t.Errorf("expected nil err, received '%v', expected -0.0054, received '%v'", err, pnl) } - if pnl, err := pos[4].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0387)) || err != nil { + if pnl := pos[4].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0387)) { t.Errorf("expected nil err, received '%v', expected 0.0387, received '%v'", err, pnl) } - if pnl, err := pos[5].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0029)) || err != nil { + if pnl := pos[5].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0029)) { t.Errorf("expected nil err, received '%v', expected -0.0029, received '%v'", err, pnl) } } @@ -1937,7 +1937,8 @@ func TestCalculatePNLFromOrders1(t *testing.T) { } positions := p.GetPositions() for i := range positions { - t.Log(positions[i].GetLatestPNLSnapshot()) + sn, _ := positions[i].GetLatestPNLSnapshot() + t.Logf("%v %v", sn.Time, positions[i].GetRealisedPNL()) } } @@ -2003,6 +2004,6 @@ func TestCalculatePNLFromOrders3(t *testing.T) { } } pos := p.GetPositions() - pnl, _ := pos[0].GetRealisedPNL() + pnl := pos[0].GetRealisedPNL() t.Logf("%v", pnl) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 8b61daf80dc..0cf897ec7da 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1305,14 +1305,14 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { } if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice { result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) - result.RealisedPNL = decimal.NewFromFloat(info.Positions[i].RealizedPNL) + result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) result.Price = decimal.NewFromFloat(info.Positions[i].Cost) return &result, nil } } } uPNL := pnl.ExchangeBasedCalculation.Amount * (pnl.ExchangeBasedCalculation.CurrentPrice - pnl.ExchangeBasedCalculation.PreviousPrice) - result.RealisedPNL = result.RealisedPNL.Add(result.UnrealisedPNL) + result.RealisedPNLBeforeFees = result.RealisedPNLBeforeFees.Add(result.UnrealisedPNL) result.UnrealisedPNL = decimal.NewFromFloat(uPNL) result.Price = decimal.NewFromFloat(pnl.ExchangeBasedCalculation.CurrentPrice) return &result, nil diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b86653b92be..8cef3df24f9 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -9,7 +9,6 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -20,7 +19,7 @@ func SetupPositionController() *PositionController { } } -// TrackNewOrder sets up the maps to then creaste a +// TrackNewOrder sets up the maps to then create a // multi position tracker which funnels down into the // position tracker, to then track an order's pnl func (c *PositionController) TrackNewOrder(d *Detail) error { @@ -190,20 +189,17 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro if err != nil { return err } - return p.UpsertPNLEntry(PNLResult{ - Time: pnl.Time, - UnrealisedPNL: pnl.UnrealisedPNL, - RealisedPNL: pnl.RealisedPNL, + return p.UpsertPNLEntry(&PNLResult{ + Time: pnl.Time, + UnrealisedPNL: pnl.UnrealisedPNL, + RealisedPNLBeforeFees: pnl.RealisedPNLBeforeFees, }) } // GetRealisedPNL returns the realised pnl if the order // is closed -func (p *PositionTracker) GetRealisedPNL() (decimal.Decimal, error) { - if p.status != Closed { - return decimal.Zero, fmt.Errorf("%v %v %v %w", p.exchange, p.asset, p.contractPair, errPositionNotClosed) - } - return p.realisedPNL, nil +func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { + return p.calculateRealisedPNL() } // GetLatestPNLSnapshot takes the latest pnl history value @@ -306,26 +302,18 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) } if err != nil { - if errors.Is(err, ErrPositionLiquidated) { - err = p.UpsertPNLEntry(*result) - if err != nil { - return err - } - // can you go into debt? - p.realisedPNL = decimal.Zero - p.unrealisedPNL = decimal.Zero - p.status = Closed - // return p.liquidate() - } else { + if !errors.Is(err, ErrPositionLiquidated) { return err } + result.UnrealisedPNL = decimal.Zero + result.RealisedPNLBeforeFees = decimal.Zero + p.status = Closed } - err = p.UpsertPNLEntry(*result) - if err != nil { - return err + upsertErr := p.UpsertPNLEntry(result) + if upsertErr != nil { + return upsertErr } p.unrealisedPNL = result.UnrealisedPNL - p.realisedPNL = result.RealisedPNL if longSide.GreaterThan(shortSide) { p.currentDirection = Long @@ -342,6 +330,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) + p.realisedPNL = p.calculateRealisedPNL() } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short @@ -353,6 +342,19 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return nil } +func (p *PositionTracker) calculateRealisedPNL() decimal.Decimal { + var realisedPNL, totalFees decimal.Decimal + for i := range p.pnlHistory { + realisedPNL = realisedPNL.Add(p.pnlHistory[i].RealisedPNLBeforeFees) + totalFees = totalFees.Add(p.pnlHistory[i].Fee) + } + if realisedPNL.IsZero() { + return decimal.Zero + } + fullyDone := realisedPNL.Sub(totalFees) + return fullyDone +} + // CalculatePNL this is a localised generic way of calculating open // positions' worth func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { @@ -365,25 +367,42 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) if calc.OrderBasedCalculation != nil { price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) + fee := decimal.NewFromFloat(calc.OrderBasedCalculation.Fee) if (p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && - p.exposure.LessThan(decimal.NewFromFloat(calc.OrderBasedCalculation.Amount)) { - // this needs reworking to handle the exposure part of things now that I've done otherwise! - one := p.exposure.Sub(amount) - result, err = p.calculatePNLAndSwapSides(calc, one, price) + p.exposure.LessThan(amount) { + + // latest order swaps directions! + first := amount.Sub(p.exposure) + second := p.exposure.Sub(amount).Abs() + fee = fee.Div(decimal.NewFromInt(2)) + result, err = p.calculatePNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, first, price, fee) if err != nil { - return result, err + return nil, err } - two := amount.Sub(p.exposure) - result, err = p.calculatePNLAndSwapSides(calc, two, price) + err = p.UpsertPNLEntry(result) if err != nil { - return result, err + return nil, err + } + + if calc.OrderBasedCalculation.Side.IsLong() { + calc.OrderBasedCalculation.Side = Short + } else if calc.OrderBasedCalculation.Side.IsShort() { + calc.OrderBasedCalculation.Side = Long + } + if p.openingDirection.IsLong() { + p.openingDirection = Short + } else if p.openingDirection.IsShort() { + p.openingDirection = Long + } + + p.entryPrice = price + result, err = p.calculatePNL(calc.OrderBasedCalculation.Date.Add(1), calc.OrderBasedCalculation.Side, second, price, fee) + if err != nil { + return nil, err } - result.UnrealisedPNL = result.UnrealisedPNL.Sub(decimal.NewFromFloat(calc.OrderBasedCalculation.Fee)) - result.RealisedPNL = result.RealisedPNL.Sub(decimal.NewFromFloat(calc.OrderBasedCalculation.Fee)) return result, nil } - - result, err = p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, amount, price, decimal.NewFromFloat(calc.OrderBasedCalculation.Fee)) + result, err = p.calculatePNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, amount, price, fee) if err != nil { return nil, err } @@ -401,47 +420,27 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) return nil, errMissingPNLCalculationFunctions } -// calculatePNLAndSwapSides is used to help calculate PNL when -// a new position flips the direction without closing the overall position -func (p *PositionTracker) calculatePNLAndSwapSides(calc *PNLCalculator, value, price decimal.Decimal) (*PNLResult, error) { - result, err := p.calculateUnrealisedPNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, value, price, decimal.Zero) - if err != nil { - return nil, err - } - if calc.OrderBasedCalculation.Side.IsShort() { - calc.OrderBasedCalculation.Side = Long - } else if calc.OrderBasedCalculation.Side.IsLong() { - calc.OrderBasedCalculation.Side = Short - } - return result, nil -} - -/// IT SEEMS LIKE ITS CURRENT EXPOSURE, TIMES THE PRICE -func (p *PositionTracker) calculateUnrealisedPNL(t time.Time, side Side, amount, price, fee decimal.Decimal) (*PNLResult, error) { +func (p *PositionTracker) calculatePNL(t time.Time, side Side, amount, price, fee decimal.Decimal) (*PNLResult, error) { var previousPNL *PNLResult if len(p.pnlHistory) > 0 { previousPNL = &p.pnlHistory[len(p.pnlHistory)-1] } - cost := amount.Mul(price) - var prevExposure, prevRealisedPNL decimal.Decimal + var prevExposure decimal.Decimal if previousPNL != nil { - prevRealisedPNL = previousPNL.RealisedPNL prevExposure = previousPNL.Exposure } var currentExposure, realisedPNL, unrealisedPNL, first, second decimal.Decimal if p.openingDirection.IsLong() { first = price if previousPNL != nil { - second = p.entryPrice + second = previousPNL.Price } } else if p.openingDirection.IsShort() { - first = p.entryPrice if previousPNL != nil { + first = previousPNL.Price second = price } } - hi := price.InexactFloat64() - log.Debugf(log.RequestSys, "%v", hi) switch { case p.currentDirection.IsShort() && side.IsShort(), p.currentDirection.IsLong() && side.IsLong(): @@ -454,47 +453,42 @@ func (p *PositionTracker) calculateUnrealisedPNL(t time.Time, side Side, amount, currentExposure = prevExposure.Sub(amount) unrealisedPNL = currentExposure.Mul(first.Sub(second)) step1 := first.Sub(second) - step2 := amount.Mul(step1) - realisedPNL = prevRealisedPNL.Add(step2) + realisedPNL = amount.Mul(step1) default: - return nil, fmt.Errorf("%v %v %v %v %w", p.currentDirection, side, currentExposure, cost, errCannotCalculateUnrealisedPNL) + return nil, fmt.Errorf("%v %v %v %w", p.currentDirection, side, currentExposure, errCannotCalculateUnrealisedPNL) } totalFees := fee for i := range p.pnlHistory { totalFees = totalFees.Add(p.pnlHistory[i].Fee) } - if !unrealisedPNL.IsZero() { unrealisedPNL = unrealisedPNL.Sub(totalFees) } - if !realisedPNL.IsZero() { - realisedPNL = realisedPNL.Sub(totalFees) - } response := &PNLResult{ - Time: t, - UnrealisedPNL: unrealisedPNL, - RealisedPNL: realisedPNL, - Price: price, - Exposure: currentExposure, - Fee: fee, + Time: t, + UnrealisedPNL: unrealisedPNL, + RealisedPNLBeforeFees: realisedPNL, + Price: price, + Exposure: currentExposure, + Fee: fee, } return response, nil } // UpsertPNLEntry upserts an entry to PNLHistory field // with some basic checks -func (p *PositionTracker) UpsertPNLEntry(entry PNLResult) error { +func (p *PositionTracker) UpsertPNLEntry(entry *PNLResult) error { if entry.Time.IsZero() { return errTimeUnset } for i := range p.pnlHistory { if entry.Time.Equal(p.pnlHistory[i].Time) { - p.pnlHistory[i] = entry + p.pnlHistory[i] = *entry return nil } } - p.pnlHistory = append(p.pnlHistory, entry) + p.pnlHistory = append(p.pnlHistory, *entry) return nil } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 5e821b13825..b0bbb35d0ef 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -431,9 +431,9 @@ func TestGetLatestPNLSnapshot(t *testing.T) { } pnl := PNLResult{ - Time: time.Now(), - UnrealisedPNL: decimal.NewFromInt(1337), - RealisedPNL: decimal.NewFromInt(1337), + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1337), } pt.pnlHistory = append(pt.pnlHistory, pnl) diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 382bc99cff4..3a2fb303943 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -179,10 +179,10 @@ type ExchangeBasedCalculation struct { // PNLResult stores pnl history at a point in time type PNLResult struct { - Time time.Time - UnrealisedPNL decimal.Decimal - RealisedPNL decimal.Decimal - Price decimal.Decimal - Exposure decimal.Decimal - Fee decimal.Decimal + Time time.Time + UnrealisedPNL decimal.Decimal + RealisedPNLBeforeFees decimal.Decimal + Price decimal.Decimal + Exposure decimal.Decimal + Fee decimal.Decimal } From 64ac8309080a1e8503ada4b32c4b4961a1a583dc Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 24 Dec 2021 14:50:11 +1100 Subject: [PATCH 046/171] Adds locks, adds rpc function --- engine/rpcserver.go | 78 ++ exchanges/order/futures.go | 203 ++- exchanges/order/futures_test.go | 6 +- exchanges/order/futures_types.go | 25 + exchanges/order/orders.go | 10 + gctrpc/rpc.pb.go | 2164 +++++++++++++++++------------- gctrpc/rpc.pb.gw.go | 83 ++ gctrpc/rpc.proto | 31 + gctrpc/rpc.swagger.json | 124 ++ gctrpc/rpc_grpc.pb.go | 36 + 10 files changed, 1799 insertions(+), 961 deletions(-) diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 810785cc78f..140069a2389 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4106,3 +4106,81 @@ func (s *RPCServer) CurrencyStateTradingPair(_ context.Context, r *gctrpc.Curren cp, asset.Item(r.Asset)) } + +// GetFuturesPositions returns pnl positions for an exchange asset pair +func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesPositionsRequest) (*gctrpc.GetFuturesPositionsResponse, error) { + exch, err := s.GetExchangeByName(r.Exchange) + if err != nil { + return nil, err + } + + cp, err := currency.NewPairFromString(r.Pair.String()) + if err != nil { + return nil, err + } + + a := asset.Item(r.Asset) + err = checkParams(r.Exchange, exch, a, cp) + if err != nil { + return nil, err + } + + pos, err := s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) + if err != nil { + return nil, err + } + response := &gctrpc.GetFuturesPositionsResponse{} + for i := range pos { + stats := pos[i].GetStats() + response.TotalOrders += int64(len(stats.Orders)) + details := &gctrpc.FuturePosition{ + Status: stats.Status.String(), + CurrentDirection: stats.LatestDirection.String(), + UnrealisedPNL: stats.UnrealisedPNL.String(), + RealisedPNL: stats.RealisedPNL.String(), + } + if len(stats.PNLHistory) > 0 { + details.OpeningDate = stats.PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) + if stats.Status == order.Closed { + details.ClosingDate = stats.PNLHistory[len(stats.PNLHistory)-1].Time.Format(common.SimpleTimeFormatWithTimezone) + } + } + for j := range stats.Orders { + var trades []*gctrpc.TradeHistory + for k := range stats.Orders[j].Trades { + trades = append(trades, &gctrpc.TradeHistory{ + CreationTime: stats.Orders[j].Trades[k].Timestamp.Unix(), + Id: stats.Orders[j].Trades[k].TID, + Price: stats.Orders[j].Trades[k].Price, + Amount: stats.Orders[j].Trades[k].Amount, + Exchange: stats.Orders[j].Trades[k].Exchange, + AssetType: stats.Asset.String(), + OrderSide: stats.Orders[j].Trades[k].Side.String(), + Fee: stats.Orders[j].Trades[k].Fee, + Total: stats.Orders[j].Trades[k].Total, + }) + } + details.Orders = append(details.Orders, &gctrpc.OrderDetails{ + Exchange: stats.Orders[j].Exchange, + Id: stats.Orders[j].ID, + ClientOrderId: stats.Orders[j].ClientOrderID, + BaseCurrency: stats.Orders[j].Pair.Base.String(), + QuoteCurrency: stats.Orders[j].Pair.Quote.String(), + AssetType: stats.Orders[j].AssetType.String(), + OrderSide: stats.Orders[j].Side.String(), + OrderType: stats.Orders[j].Type, + CreationTime: stats.Orders[j].Date.Unix(), + UpdateTime: stats.Orders[j].LastUpdated.Unix(), + Status: stats.Orders[j].Status.String(), + Price: stats.Orders[j].Price, + Amount: stats.Orders[j].Amount, + Fee: stats.Orders[j].Fee, + Cost: stats.Orders[j].Cost, + Trades: trades, + }) + } + response.Positions = append(response.Positions, details) + } + + return response, nil +} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 8cef3df24f9..2f9b4aa8bde 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -7,6 +7,7 @@ import ( "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -29,6 +30,10 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { if !d.AssetType.IsFutures() { return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, errNotFutureAsset) } + if c == nil { + return common.ErrNilPointer + } + c.m.Lock() if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)]; !ok { c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) } @@ -43,13 +48,38 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { Underlying: d.Pair.Base, }) if err != nil { + c.m.Unlock() return err } c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] = ptc } + c.m.Unlock() return c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair].TrackNewOrder(d) } +func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*PositionTracker, error) { + if c == nil { + return nil, common.ErrNilPointer + } + c.m.Lock() + exchM, ok := c.positionTrackerControllers[exch] + if !ok { + return nil, errExchangeNotFound + } + itemM, ok := exchM[item] + if !ok { + c.m.Unlock() + return nil, errAssetNotFound + } + multiPositionTracker, ok := itemM[pair] + if !ok { + c.m.Unlock() + return nil, currency.ErrPairNotFound + } + c.m.Unlock() + return multiPositionTracker.GetPositions(), nil +} + // SetupMultiPositionTracker creates a futures order tracker for a specific exchange func SetupMultiPositionTracker(setup *PositionControllerSetup) (*MultiPositionTracker, error) { if setup == nil { @@ -84,30 +114,43 @@ func SetupMultiPositionTracker(setup *PositionControllerSetup) (*MultiPositionTr // GetPositions returns all positions func (e *MultiPositionTracker) GetPositions() []*PositionTracker { + if e == nil { + return nil + } + e.m.Lock() + defer e.m.Unlock() return e.positions } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { + if e == nil { + return common.ErrNilPointer + } if d == nil { return ErrSubmissionIsNil } + e.m.Lock() + e.m.Unlock() if d.AssetType != e.asset { return errAssetMismatch } if tracker, ok := e.orderPositions[d.ID]; ok { // this has already been associated // update the tracker + e.m.Unlock() return tracker.TrackNewOrder(d) } if len(e.positions) > 0 { for i := range e.positions { if e.positions[i].status == Open && i != len(e.positions)-1 { + e.m.Unlock() return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) } } if e.positions[len(e.positions)-1].status == Open { + e.m.Unlock() err := e.positions[len(e.positions)-1].TrackNewOrder(d) if err != nil && !errors.Is(err, errPositionClosed) { return err @@ -126,10 +169,12 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { } tracker, err := e.SetupPositionTracker(setup) if err != nil { + e.m.Unlock() return err } e.positions = append(e.positions, tracker) + e.m.Unlock() err = tracker.TrackNewOrder(d) if err != nil { return err @@ -141,6 +186,9 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { + if e == nil { + return nil, common.ErrNilPointer + } if e.exchange == "" { return nil, errExchangeNameEmpty } @@ -174,9 +222,35 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) return resp, nil } +func (p *PositionTracker) GetStats() PositionStats { + if p == nil { + return PositionStats{} + } + p.m.Lock() + defer p.m.Unlock() + return PositionStats{ + Exchange: p.exchange, + Asset: p.asset, + Pair: p.contractPair, + Underlying: p.underlyingAsset, + Status: p.status, + Orders: append(p.longPositions, p.shortPositions...), + RealisedPNL: p.realisedPNL, + UnrealisedPNL: p.unrealisedPNL, + LatestDirection: p.currentDirection, + OpeningDirection: p.openingDirection, + OpeningPrice: p.entryPrice, + LatestPrice: p.latestPrice, + PNLHistory: p.pnlHistory, + } +} + // TrackPNLByTime calculates the PNL based on a position tracker's exposure // and current pricing. Adds the entry to PNL history to track over time func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) error { + if p == nil { + return common.ErrNilPointer + } defer func() { p.latestPrice = decimal.NewFromFloat(currentPrice) }() @@ -189,7 +263,7 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro if err != nil { return err } - return p.UpsertPNLEntry(&PNLResult{ + return p.upsertPNLEntry(&PNLResult{ Time: pnl.Time, UnrealisedPNL: pnl.UnrealisedPNL, RealisedPNLBeforeFees: pnl.RealisedPNLBeforeFees, @@ -214,6 +288,11 @@ func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { // TrackNewOrder knows how things are going for a given // futures contract func (p *PositionTracker) TrackNewOrder(d *Detail) error { + if p == nil { + return common.ErrNilPointer + } + p.m.Lock() + defer p.m.Unlock() if p.status == Closed { return errPositionClosed } @@ -297,8 +376,10 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if len(p.pnlHistory) != 0 { cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price.InexactFloat64() } + p.m.Unlock() result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{ExchangeBasedCalculation: cal}) } else { + p.m.Unlock() result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) } if err != nil { @@ -309,7 +390,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { result.RealisedPNLBeforeFees = decimal.Zero p.status = Closed } - upsertErr := p.UpsertPNLEntry(result) + upsertErr := p.upsertPNLEntry(result) if upsertErr != nil { return upsertErr } @@ -342,25 +423,18 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return nil } -func (p *PositionTracker) calculateRealisedPNL() decimal.Decimal { - var realisedPNL, totalFees decimal.Decimal - for i := range p.pnlHistory { - realisedPNL = realisedPNL.Add(p.pnlHistory[i].RealisedPNLBeforeFees) - totalFees = totalFees.Add(p.pnlHistory[i].Fee) - } - if realisedPNL.IsZero() { - return decimal.Zero - } - fullyDone := realisedPNL.Sub(totalFees) - return fullyDone -} - // CalculatePNL this is a localised generic way of calculating open -// positions' worth +// positions' worth, it is an implementation of the PNLCalculation interface func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { + // re work this to have no dependency on "p", this way locking has no impact here and its less messy + if p == nil { + return nil, common.ErrNilPointer + } if calc == nil { return nil, ErrNilPNLCalculator } + p.m.Lock() + defer p.m.Unlock() result := &PNLResult{} var price, amount decimal.Decimal var err error @@ -375,11 +449,19 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) first := amount.Sub(p.exposure) second := p.exposure.Sub(amount).Abs() fee = fee.Div(decimal.NewFromInt(2)) - result, err = p.calculatePNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, first, price, fee) + result, err = createPNLResult( + calc.OrderBasedCalculation.Date, + calc.OrderBasedCalculation.Side, + first, + price, + fee, + p.openingDirection, + p.currentDirection, + p.pnlHistory) if err != nil { return nil, err } - err = p.UpsertPNLEntry(result) + err = p.upsertPNLEntry(result) if err != nil { return nil, err } @@ -396,13 +478,29 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) } p.entryPrice = price - result, err = p.calculatePNL(calc.OrderBasedCalculation.Date.Add(1), calc.OrderBasedCalculation.Side, second, price, fee) + result, err = createPNLResult( + calc.OrderBasedCalculation.Date.Add(1), + calc.OrderBasedCalculation.Side, + second, + price, + fee, + p.openingDirection, + p.currentDirection, + p.pnlHistory) if err != nil { return nil, err } return result, nil } - result, err = p.calculatePNL(calc.OrderBasedCalculation.Date, calc.OrderBasedCalculation.Side, amount, price, fee) + result, err = createPNLResult( + calc.OrderBasedCalculation.Date, + calc.OrderBasedCalculation.Side, + amount, + price, + fee, + p.openingDirection, + p.currentDirection, + p.pnlHistory) if err != nil { return nil, err } @@ -414,52 +512,56 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) diff := price.Sub(p.entryPrice) result.UnrealisedPNL = p.exposure.Mul(diff) result.Price = price + if len(p.pnlHistory) > 0 { + result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees + result.Exposure = p.pnlHistory[len(p.pnlHistory)-1].Exposure + } return result, nil } return nil, errMissingPNLCalculationFunctions } -func (p *PositionTracker) calculatePNL(t time.Time, side Side, amount, price, fee decimal.Decimal) (*PNLResult, error) { +func createPNLResult(t time.Time, side Side, amount, price, fee decimal.Decimal, openingDirection, currentDirection Side, pnlHistory []PNLResult) (*PNLResult, error) { var previousPNL *PNLResult - if len(p.pnlHistory) > 0 { - previousPNL = &p.pnlHistory[len(p.pnlHistory)-1] + if len(pnlHistory) > 0 { + previousPNL = &pnlHistory[len(pnlHistory)-1] } var prevExposure decimal.Decimal if previousPNL != nil { prevExposure = previousPNL.Exposure } var currentExposure, realisedPNL, unrealisedPNL, first, second decimal.Decimal - if p.openingDirection.IsLong() { + if openingDirection.IsLong() { first = price if previousPNL != nil { second = previousPNL.Price } - } else if p.openingDirection.IsShort() { + } else if openingDirection.IsShort() { if previousPNL != nil { first = previousPNL.Price second = price } } switch { - case p.currentDirection.IsShort() && side.IsShort(), - p.currentDirection.IsLong() && side.IsLong(): + case currentDirection.IsShort() && side.IsShort(), + currentDirection.IsLong() && side.IsLong(): // appending to your position currentExposure = prevExposure.Add(amount) unrealisedPNL = currentExposure.Mul(first.Sub(second)) - case p.currentDirection.IsShort() && side.IsLong(), - p.currentDirection.IsLong() && side.IsShort(): + case currentDirection.IsShort() && side.IsLong(), + currentDirection.IsLong() && side.IsShort(): // selling/closing your position by "amount" currentExposure = prevExposure.Sub(amount) unrealisedPNL = currentExposure.Mul(first.Sub(second)) step1 := first.Sub(second) realisedPNL = amount.Mul(step1) default: - return nil, fmt.Errorf("%v %v %v %w", p.currentDirection, side, currentExposure, errCannotCalculateUnrealisedPNL) + return nil, fmt.Errorf("%v %v %v %w", currentDirection, side, currentExposure, errCannotCalculateUnrealisedPNL) } totalFees := fee - for i := range p.pnlHistory { - totalFees = totalFees.Add(p.pnlHistory[i].Fee) + for i := range pnlHistory { + totalFees = totalFees.Add(pnlHistory[i].Fee) } if !unrealisedPNL.IsZero() { unrealisedPNL = unrealisedPNL.Sub(totalFees) @@ -476,9 +578,32 @@ func (p *PositionTracker) calculatePNL(t time.Time, side Side, amount, price, fe return response, nil } -// UpsertPNLEntry upserts an entry to PNLHistory field +func (p *PositionTracker) calculateRealisedPNL() decimal.Decimal { + if p == nil { + return decimal.Zero + } + p.m.Lock() + defer p.m.Unlock() + var realisedPNL, totalFees decimal.Decimal + for i := range p.pnlHistory { + realisedPNL = realisedPNL.Add(p.pnlHistory[i].RealisedPNLBeforeFees) + totalFees = totalFees.Add(p.pnlHistory[i].Fee) + } + if realisedPNL.IsZero() { + return decimal.Zero + } + fullyDone := realisedPNL.Sub(totalFees) + return fullyDone +} + +// upsertPNLEntry upserts an entry to PNLHistory field // with some basic checks -func (p *PositionTracker) UpsertPNLEntry(entry *PNLResult) error { +func (p *PositionTracker) upsertPNLEntry(entry *PNLResult) error { + if p == nil { + return common.ErrNilPointer + } + p.m.Lock() + defer p.m.Unlock() if entry.Time.IsZero() { return errTimeUnset } @@ -491,13 +616,3 @@ func (p *PositionTracker) UpsertPNLEntry(entry *PNLResult) error { p.pnlHistory = append(p.pnlHistory, *entry) return nil } - -// IsShort returns if the side is short -func (s Side) IsShort() bool { - return s == Short || s == Sell -} - -// IsLong returns if the side is long -func (s Side) IsLong() bool { - return s == Long || s == Buy -} diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index b0bbb35d0ef..2c556d9c13a 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -63,12 +63,12 @@ func TestTrackPNL(t *testing.T) { func TestUpsertPNLEntry(t *testing.T) { t.Parallel() f := &PositionTracker{} - err := f.UpsertPNLEntry(PNLResult{}) + err := f.upsertPNLEntry(PNLResult{}) if !errors.Is(err, errTimeUnset) { t.Error(err) } tt := time.Now() - err = f.UpsertPNLEntry(PNLResult{Time: tt}) + err = f.upsertPNLEntry(PNLResult{Time: tt}) if !errors.Is(err, nil) { t.Error(err) } @@ -76,7 +76,7 @@ func TestUpsertPNLEntry(t *testing.T) { t.Errorf("expected 1 received %v", len(f.pnlHistory)) } - err = f.UpsertPNLEntry(PNLResult{Time: tt}) + err = f.upsertPNLEntry(PNLResult{Time: tt}) if !errors.Is(err, nil) { t.Error(err) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 3a2fb303943..197a0240a6d 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -2,6 +2,7 @@ package order import ( "errors" + "sync" "time" "github.com/shopspring/decimal" @@ -24,6 +25,8 @@ var ( errNilOrder = errors.New("nil order received") errNoPNLHistory = errors.New("no pnl history") errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL, order is not open") + errExchangeNotFound = errors.New("exchange not found") + errAssetNotFound = errors.New("asset not found") // ErrNilPNLCalculator is raised when pnl calculation is requested for // an exchange, but the fields are not set properly @@ -53,6 +56,7 @@ type CollateralManagement interface { // and so all you need to do is send all orders to // the position controller and its all tracked happily type PositionController struct { + m sync.Mutex positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker } @@ -61,6 +65,7 @@ type PositionController struct { // is closed, then the position controller will create a new one // to track the current positions type MultiPositionTracker struct { + m sync.Mutex exchange string asset asset.Item pair currency.Pair @@ -95,6 +100,7 @@ type PositionControllerSetup struct { // completely within this position tracker, however, can still provide a good // timeline of performance until the position is closed type PositionTracker struct { + m sync.Mutex exchange string asset asset.Item contractPair currency.Pair @@ -175,6 +181,7 @@ type ExchangeBasedCalculation struct { Time time.Time OrderID string Fee decimal.Decimal + PNLHistory []PNLResult } // PNLResult stores pnl history at a point in time @@ -186,3 +193,21 @@ type PNLResult struct { Exposure decimal.Decimal Fee decimal.Decimal } + +// PositionStats is a basic holder +// for position information +type PositionStats struct { + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + Orders []Detail + RealisedPNL decimal.Decimal + UnrealisedPNL decimal.Decimal + LatestDirection Side + Status Status + OpeningDirection Side + OpeningPrice decimal.Decimal + LatestPrice decimal.Decimal + PNLHistory []PNLResult +} diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index c0b7960e675..94a232e9a5b 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -483,6 +483,16 @@ func (s Side) Title() string { return strings.Title(strings.ToLower(string(s))) } +// IsShort returns if the side is short +func (s Side) IsShort() bool { + return s == Short || s == Sell +} + +// IsLong returns if the side is long +func (s Side) IsLong() bool { + return s == Long || s == Buy +} + // String implements the stringer interface func (s Status) String() string { return string(s) diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 5f451c79660..ae010783861 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.17.3 +// protoc v3.15.1 // source: rpc.proto package gctrpc @@ -10765,6 +10765,251 @@ func (x *CurrencyState) GetTradingEnabled() bool { return false } +type GetFuturesPositionsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,3,opt,name=pair,proto3" json:"pair,omitempty"` + StartDate string `protobuf:"bytes,4,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` + EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` + Status int64 `protobuf:"varint,6,opt,name=status,proto3" json:"status,omitempty"` + PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` +} + +func (x *GetFuturesPositionsRequest) Reset() { + *x = GetFuturesPositionsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[169] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFuturesPositionsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFuturesPositionsRequest) ProtoMessage() {} + +func (x *GetFuturesPositionsRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[169] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFuturesPositionsRequest.ProtoReflect.Descriptor instead. +func (*GetFuturesPositionsRequest) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{169} +} + +func (x *GetFuturesPositionsRequest) GetExchange() string { + if x != nil { + return x.Exchange + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetAsset() string { + if x != nil { + return x.Asset + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetPair() *CurrencyPair { + if x != nil { + return x.Pair + } + return nil +} + +func (x *GetFuturesPositionsRequest) GetStartDate() string { + if x != nil { + return x.StartDate + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetEndDate() string { + if x != nil { + return x.EndDate + } + return "" +} + +func (x *GetFuturesPositionsRequest) GetStatus() int64 { + if x != nil { + return x.Status + } + return 0 +} + +func (x *GetFuturesPositionsRequest) GetPositionLimit() int64 { + if x != nil { + return x.PositionLimit + } + return 0 +} + +type GetFuturesPositionsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` + Positions []*FuturePosition `protobuf:"bytes,2,rep,name=positions,proto3" json:"positions,omitempty"` +} + +func (x *GetFuturesPositionsResponse) Reset() { + *x = GetFuturesPositionsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[170] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetFuturesPositionsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetFuturesPositionsResponse) ProtoMessage() {} + +func (x *GetFuturesPositionsResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[170] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFuturesPositionsResponse.ProtoReflect.Descriptor instead. +func (*GetFuturesPositionsResponse) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{170} +} + +func (x *GetFuturesPositionsResponse) GetTotalOrders() int64 { + if x != nil { + return x.TotalOrders + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetPositions() []*FuturePosition { + if x != nil { + return x.Positions + } + return nil +} + +type FuturePosition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + CurrentDirection string `protobuf:"bytes,2,opt,name=currentDirection,proto3" json:"currentDirection,omitempty"` + UnrealisedPNL string `protobuf:"bytes,3,opt,name=unrealisedPNL,proto3" json:"unrealisedPNL,omitempty"` + RealisedPNL string `protobuf:"bytes,4,opt,name=realisedPNL,proto3" json:"realisedPNL,omitempty"` + OpeningDate string `protobuf:"bytes,5,opt,name=openingDate,proto3" json:"openingDate,omitempty"` + ClosingDate string `protobuf:"bytes,6,opt,name=closingDate,proto3" json:"closingDate,omitempty"` + Orders []*OrderDetails `protobuf:"bytes,7,rep,name=orders,proto3" json:"orders,omitempty"` +} + +func (x *FuturePosition) Reset() { + *x = FuturePosition{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[171] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FuturePosition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FuturePosition) ProtoMessage() {} + +func (x *FuturePosition) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[171] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FuturePosition.ProtoReflect.Descriptor instead. +func (*FuturePosition) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{171} +} + +func (x *FuturePosition) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *FuturePosition) GetCurrentDirection() string { + if x != nil { + return x.CurrentDirection + } + return "" +} + +func (x *FuturePosition) GetUnrealisedPNL() string { + if x != nil { + return x.UnrealisedPNL + } + return "" +} + +func (x *FuturePosition) GetRealisedPNL() string { + if x != nil { + return x.RealisedPNL + } + return "" +} + +func (x *FuturePosition) GetOpeningDate() string { + if x != nil { + return x.OpeningDate + } + return "" +} + +func (x *FuturePosition) GetClosingDate() string { + if x != nil { + return x.ClosingDate + } + return "" +} + +func (x *FuturePosition) GetOrders() []*OrderDetails { + if x != nil { + return x.Orders + } + return nil +} + type CancelBatchOrdersResponse_Orders struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -10776,7 +11021,7 @@ type CancelBatchOrdersResponse_Orders struct { func (x *CancelBatchOrdersResponse_Orders) Reset() { *x = CancelBatchOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[179] + mi := &file_rpc_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10789,7 +11034,7 @@ func (x *CancelBatchOrdersResponse_Orders) String() string { func (*CancelBatchOrdersResponse_Orders) ProtoMessage() {} func (x *CancelBatchOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[179] + mi := &file_rpc_proto_msgTypes[182] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10824,7 +11069,7 @@ type CancelAllOrdersResponse_Orders struct { func (x *CancelAllOrdersResponse_Orders) Reset() { *x = CancelAllOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[181] + mi := &file_rpc_proto_msgTypes[184] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10837,7 +11082,7 @@ func (x *CancelAllOrdersResponse_Orders) String() string { func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} func (x *CancelAllOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[181] + mi := &file_rpc_proto_msgTypes[184] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12305,709 +12550,756 @@ var file_rpc_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x32, 0x99, 0x57, 0x0a, 0x0e, - 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, - 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, - 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, - 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xf0, 0x01, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x75, + 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, + 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, + 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, + 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, + 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, + 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, + 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, + 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x32, 0x9a, 0x58, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, + 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, - 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, - 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, + 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, + 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, + 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, + 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, + 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, + 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, + 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, + 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, + 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, + 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, + 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, + 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, - 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, - 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, - 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, - 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, + 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, + 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, + 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, + 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, + 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, + 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, + 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, + 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, + 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, - 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, - 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, - 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, + 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, + 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, + 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, + 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, + 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, + 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, + 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, - 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, + 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, + 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, - 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, - 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, + 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, + 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, + 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, + 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, + 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, + 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, - 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, - 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, + 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, - 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, + 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, - 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, + 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, + 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, - 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, - 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, + 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, + 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, - 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, - 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, - 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, - 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, - 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, + 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, + 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, + 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, + 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, - 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, + 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, + 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, + 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, + 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, + 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, + 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, + 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, + 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, + 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, + 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, + 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, + 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, + 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, + 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, + 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, + 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, - 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, - 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, - 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, + 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, + 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, + 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, + 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, + 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, + 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, + 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, + 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, + 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, + 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, + 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, + 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, + 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, - 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, + 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, + 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, - 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, - 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, + 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, + 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, - 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, + 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, + 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, + 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, + 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, + 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, + 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, + 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, + 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, - 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, - 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, - 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, - 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, + 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, + 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -13022,7 +13314,7 @@ func file_rpc_proto_rawDescGZIP() []byte { return file_rpc_proto_rawDescData } -var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 185) +var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 188) var file_rpc_proto_goTypes = []interface{}{ (*GetInfoRequest)(nil), // 0: gctrpc.GetInfoRequest (*GetInfoResponse)(nil), // 1: gctrpc.GetInfoResponse @@ -13193,32 +13485,35 @@ var file_rpc_proto_goTypes = []interface{}{ (*CurrencyStateDepositRequest)(nil), // 166: gctrpc.CurrencyStateDepositRequest (*CurrencyStateResponse)(nil), // 167: gctrpc.CurrencyStateResponse (*CurrencyState)(nil), // 168: gctrpc.CurrencyState - nil, // 169: gctrpc.GetInfoResponse.SubsystemStatusEntry - nil, // 170: gctrpc.GetInfoResponse.RpcEndpointsEntry - nil, // 171: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - nil, // 172: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - nil, // 173: gctrpc.GetRPCEndpointsResponse.EndpointsEntry - nil, // 174: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - nil, // 175: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - nil, // 176: gctrpc.OnlineCoins.CoinsEntry - nil, // 177: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - nil, // 178: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - (*CancelBatchOrdersResponse_Orders)(nil), // 179: gctrpc.CancelBatchOrdersResponse.Orders - nil, // 180: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - (*CancelAllOrdersResponse_Orders)(nil), // 181: gctrpc.CancelAllOrdersResponse.Orders - nil, // 182: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - nil, // 183: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - nil, // 184: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - (*timestamppb.Timestamp)(nil), // 185: google.protobuf.Timestamp + (*GetFuturesPositionsRequest)(nil), // 169: gctrpc.GetFuturesPositionsRequest + (*GetFuturesPositionsResponse)(nil), // 170: gctrpc.GetFuturesPositionsResponse + (*FuturePosition)(nil), // 171: gctrpc.FuturePosition + nil, // 172: gctrpc.GetInfoResponse.SubsystemStatusEntry + nil, // 173: gctrpc.GetInfoResponse.RpcEndpointsEntry + nil, // 174: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + nil, // 175: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + nil, // 176: gctrpc.GetRPCEndpointsResponse.EndpointsEntry + nil, // 177: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + nil, // 178: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + nil, // 179: gctrpc.OnlineCoins.CoinsEntry + nil, // 180: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + nil, // 181: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + (*CancelBatchOrdersResponse_Orders)(nil), // 182: gctrpc.CancelBatchOrdersResponse.Orders + nil, // 183: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + (*CancelAllOrdersResponse_Orders)(nil), // 184: gctrpc.CancelAllOrdersResponse.Orders + nil, // 185: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + nil, // 186: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + nil, // 187: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + (*timestamppb.Timestamp)(nil), // 188: google.protobuf.Timestamp } var file_rpc_proto_depIdxs = []int32{ - 169, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry - 170, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry - 171, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - 172, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - 173, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry - 174, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - 175, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + 172, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry + 173, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry + 174, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + 175, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + 176, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry + 177, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + 178, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry 21, // 7: gctrpc.GetTickerRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 8: gctrpc.TickerResponse.pair:type_name -> gctrpc.CurrencyPair 22, // 9: gctrpc.Tickers.tickers:type_name -> gctrpc.TickerResponse @@ -13233,12 +13528,12 @@ var file_rpc_proto_depIdxs = []int32{ 33, // 18: gctrpc.GetAccountInfoResponse.accounts:type_name -> gctrpc.Account 38, // 19: gctrpc.GetPortfolioResponse.portfolio:type_name -> gctrpc.PortfolioAddress 43, // 20: gctrpc.OfflineCoins.addresses:type_name -> gctrpc.OfflineCoinSummary - 176, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry + 179, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry 42, // 22: gctrpc.GetPortfolioSummaryResponse.coin_totals:type_name -> gctrpc.Coin 42, // 23: gctrpc.GetPortfolioSummaryResponse.coins_offline:type_name -> gctrpc.Coin - 177, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + 180, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry 42, // 25: gctrpc.GetPortfolioSummaryResponse.coins_online:type_name -> gctrpc.Coin - 178, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + 181, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry 51, // 27: gctrpc.GetForexProvidersResponse.forex_providers:type_name -> gctrpc.ForexProvider 54, // 28: gctrpc.GetForexRatesResponse.forex_rates:type_name -> gctrpc.ForexRatesConversion 57, // 29: gctrpc.OrderDetails.trades:type_name -> gctrpc.TradeHistory @@ -13252,23 +13547,23 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 37: gctrpc.WhaleBombRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 38: gctrpc.CancelOrderRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 39: gctrpc.CancelBatchOrdersRequest.pair:type_name -> gctrpc.CurrencyPair - 179, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders - 181, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders + 182, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders + 184, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders 73, // 42: gctrpc.GetEventsResponse.condition_params:type_name -> gctrpc.ConditionParams 21, // 43: gctrpc.GetEventsResponse.pair:type_name -> gctrpc.CurrencyPair 73, // 44: gctrpc.AddEventRequest.condition_params:type_name -> gctrpc.ConditionParams 21, // 45: gctrpc.AddEventRequest.pair:type_name -> gctrpc.CurrencyPair 79, // 46: gctrpc.DepositAddresses.addresses:type_name -> gctrpc.DepositAddress - 183, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + 186, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry 94, // 48: gctrpc.WithdrawalEventByIDResponse.event:type_name -> gctrpc.WithdrawalEventResponse 94, // 49: gctrpc.WithdrawalEventsByExchangeResponse.event:type_name -> gctrpc.WithdrawalEventResponse 95, // 50: gctrpc.WithdrawalEventResponse.exchange:type_name -> gctrpc.WithdrawlExchangeEvent 96, // 51: gctrpc.WithdrawalEventResponse.request:type_name -> gctrpc.WithdrawalRequestEvent - 185, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp - 185, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp + 188, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp + 188, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp 97, // 54: gctrpc.WithdrawalRequestEvent.fiat:type_name -> gctrpc.FiatWithdrawalEvent 98, // 55: gctrpc.WithdrawalRequestEvent.crypto:type_name -> gctrpc.CryptoWithdrawalEvent - 184, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + 187, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry 21, // 57: gctrpc.SetExchangePairRequest.pairs:type_name -> gctrpc.CurrencyPair 21, // 58: gctrpc.GetOrderbookStreamRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 59: gctrpc.GetTickerStreamRequest.pair:type_name -> gctrpc.CurrencyPair @@ -13298,210 +13593,215 @@ var file_rpc_proto_depIdxs = []int32{ 154, // 83: gctrpc.DataHistoryJobs.results:type_name -> gctrpc.DataHistoryJob 21, // 84: gctrpc.ModifyOrderRequest.pair:type_name -> gctrpc.CurrencyPair 168, // 85: gctrpc.CurrencyStateResponse.currency_states:type_name -> gctrpc.CurrencyState - 9, // 86: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 3, // 87: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer - 9, // 88: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 18, // 89: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 44, // 90: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary - 45, // 91: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins - 46, // 92: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins - 180, // 93: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - 182, // 94: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - 80, // 95: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses - 18, // 96: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 0, // 97: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest - 6, // 98: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest - 5, // 99: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 5, // 100: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 8, // 101: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest - 2, // 102: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest - 12, // 103: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest - 11, // 104: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 105: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 106: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest - 15, // 107: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest - 11, // 108: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 20, // 109: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest - 23, // 110: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest - 26, // 111: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest - 29, // 112: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest - 32, // 113: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 114: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 115: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest - 36, // 116: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest - 39, // 117: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest - 41, // 118: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest - 48, // 119: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest - 49, // 120: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest - 50, // 121: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest - 53, // 122: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest - 58, // 123: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest - 60, // 124: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest - 61, // 125: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest - 64, // 126: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest - 66, // 127: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest - 67, // 128: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest - 68, // 129: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest - 70, // 130: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest - 72, // 131: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest - 75, // 132: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest - 77, // 133: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest - 78, // 134: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest - 82, // 135: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest - 84, // 136: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest - 86, // 137: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest - 87, // 138: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest - 89, // 139: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest - 91, // 140: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest - 92, // 141: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest - 99, // 142: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest - 101, // 143: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest - 102, // 144: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest - 104, // 145: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest - 105, // 146: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest - 106, // 147: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest - 107, // 148: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest - 108, // 149: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest - 109, // 150: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest - 120, // 151: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest - 125, // 152: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest - 126, // 153: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest - 123, // 154: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest - 127, // 155: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest - 121, // 156: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest - 122, // 157: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest - 124, // 158: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest - 128, // 159: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest - 115, // 160: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest - 132, // 161: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest - 133, // 162: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest - 134, // 163: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest - 135, // 164: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest - 137, // 165: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest - 139, // 166: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest - 140, // 167: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest - 143, // 168: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest - 144, // 169: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest - 111, // 170: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 171: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 172: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest - 114, // 173: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest - 145, // 174: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest - 146, // 175: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest - 148, // 176: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest - 149, // 177: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest - 153, // 178: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 0, // 179: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest - 157, // 180: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest - 153, // 181: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 158, // 182: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest - 159, // 183: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest - 58, // 184: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest - 160, // 185: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest - 162, // 186: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest - 163, // 187: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest - 166, // 188: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest - 165, // 189: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest - 164, // 190: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest - 1, // 191: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse - 7, // 192: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse - 131, // 193: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse - 131, // 194: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse - 10, // 195: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse - 4, // 196: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse - 13, // 197: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse - 131, // 198: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse - 19, // 199: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse - 14, // 200: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse - 16, // 201: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse - 131, // 202: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse - 22, // 203: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse - 25, // 204: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse - 28, // 205: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse - 31, // 206: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse - 35, // 207: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 208: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 209: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse - 37, // 210: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse - 40, // 211: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse - 47, // 212: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse - 131, // 213: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse - 131, // 214: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse - 52, // 215: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse - 55, // 216: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse - 59, // 217: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse - 56, // 218: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails - 63, // 219: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse - 65, // 220: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse - 65, // 221: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse - 131, // 222: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse - 69, // 223: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse - 71, // 224: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse - 74, // 225: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse - 76, // 226: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse - 131, // 227: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse - 81, // 228: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse - 83, // 229: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse - 85, // 230: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse - 88, // 231: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse - 88, // 232: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse - 90, // 233: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse - 93, // 234: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 93, // 235: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 100, // 236: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 100, // 237: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 103, // 238: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse - 131, // 239: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse - 28, // 240: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse - 28, // 241: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse - 22, // 242: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse - 22, // 243: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse - 110, // 244: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse - 131, // 245: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse - 131, // 246: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse - 130, // 247: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse - 129, // 248: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse - 130, // 249: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse - 131, // 250: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse - 131, // 251: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse - 129, // 252: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse - 131, // 253: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse - 116, // 254: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 131, // 255: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse - 131, // 256: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse - 131, // 257: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse - 136, // 258: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse - 138, // 259: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse - 131, // 260: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse - 142, // 261: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse - 131, // 262: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse - 131, // 263: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse - 113, // 264: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 265: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 266: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse - 116, // 267: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 147, // 268: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 147, // 269: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 131, // 270: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse - 152, // 271: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse - 154, // 272: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob - 156, // 273: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs - 156, // 274: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs - 154, // 275: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob - 131, // 276: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse - 131, // 277: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse - 59, // 278: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse - 161, // 279: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse - 167, // 280: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse - 131, // 281: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse - 131, // 282: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse - 131, // 283: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse - 131, // 284: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse - 191, // [191:285] is the sub-list for method output_type - 97, // [97:191] is the sub-list for method input_type - 97, // [97:97] is the sub-list for extension type_name - 97, // [97:97] is the sub-list for extension extendee - 0, // [0:97] is the sub-list for field type_name + 21, // 86: gctrpc.GetFuturesPositionsRequest.pair:type_name -> gctrpc.CurrencyPair + 171, // 87: gctrpc.GetFuturesPositionsResponse.positions:type_name -> gctrpc.FuturePosition + 56, // 88: gctrpc.FuturePosition.orders:type_name -> gctrpc.OrderDetails + 9, // 89: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 3, // 90: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer + 9, // 91: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 18, // 92: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 44, // 93: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary + 45, // 94: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins + 46, // 95: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins + 183, // 96: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + 185, // 97: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + 80, // 98: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses + 18, // 99: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 0, // 100: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest + 6, // 101: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest + 5, // 102: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 5, // 103: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 8, // 104: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest + 2, // 105: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest + 12, // 106: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest + 11, // 107: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 108: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 109: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest + 15, // 110: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest + 11, // 111: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 20, // 112: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest + 23, // 113: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest + 26, // 114: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest + 29, // 115: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest + 32, // 116: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 117: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 118: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest + 36, // 119: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest + 39, // 120: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest + 41, // 121: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest + 48, // 122: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest + 49, // 123: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest + 50, // 124: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest + 53, // 125: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest + 58, // 126: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest + 60, // 127: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest + 61, // 128: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest + 64, // 129: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest + 66, // 130: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest + 67, // 131: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest + 68, // 132: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest + 70, // 133: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest + 72, // 134: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest + 75, // 135: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest + 77, // 136: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest + 78, // 137: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest + 82, // 138: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest + 84, // 139: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest + 86, // 140: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest + 87, // 141: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest + 89, // 142: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest + 91, // 143: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest + 92, // 144: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest + 99, // 145: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest + 101, // 146: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest + 102, // 147: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest + 104, // 148: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest + 105, // 149: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest + 106, // 150: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest + 107, // 151: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest + 108, // 152: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest + 109, // 153: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest + 120, // 154: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest + 125, // 155: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest + 126, // 156: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest + 123, // 157: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest + 127, // 158: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest + 121, // 159: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest + 122, // 160: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest + 124, // 161: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest + 128, // 162: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest + 115, // 163: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest + 132, // 164: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest + 133, // 165: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest + 134, // 166: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest + 135, // 167: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest + 137, // 168: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest + 139, // 169: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest + 140, // 170: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest + 143, // 171: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest + 144, // 172: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest + 111, // 173: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 174: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 175: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest + 114, // 176: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest + 145, // 177: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest + 146, // 178: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest + 148, // 179: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest + 149, // 180: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest + 153, // 181: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 0, // 182: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest + 157, // 183: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest + 153, // 184: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 158, // 185: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest + 159, // 186: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest + 58, // 187: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest + 160, // 188: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest + 162, // 189: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest + 163, // 190: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest + 166, // 191: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest + 165, // 192: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest + 164, // 193: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest + 169, // 194: gctrpc.GoCryptoTrader.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest + 1, // 195: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse + 7, // 196: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse + 131, // 197: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse + 131, // 198: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse + 10, // 199: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse + 4, // 200: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse + 13, // 201: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse + 131, // 202: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse + 19, // 203: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse + 14, // 204: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse + 16, // 205: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse + 131, // 206: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse + 22, // 207: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse + 25, // 208: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse + 28, // 209: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse + 31, // 210: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse + 35, // 211: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 212: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 213: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse + 37, // 214: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse + 40, // 215: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse + 47, // 216: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse + 131, // 217: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse + 131, // 218: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse + 52, // 219: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse + 55, // 220: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse + 59, // 221: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse + 56, // 222: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails + 63, // 223: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse + 65, // 224: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse + 65, // 225: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse + 131, // 226: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse + 69, // 227: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse + 71, // 228: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse + 74, // 229: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse + 76, // 230: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse + 131, // 231: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse + 81, // 232: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse + 83, // 233: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse + 85, // 234: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse + 88, // 235: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse + 88, // 236: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse + 90, // 237: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse + 93, // 238: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 93, // 239: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 100, // 240: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 100, // 241: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 103, // 242: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse + 131, // 243: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse + 28, // 244: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse + 28, // 245: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse + 22, // 246: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse + 22, // 247: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse + 110, // 248: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse + 131, // 249: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse + 131, // 250: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse + 130, // 251: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse + 129, // 252: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse + 130, // 253: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse + 131, // 254: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse + 131, // 255: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse + 129, // 256: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse + 131, // 257: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse + 116, // 258: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 131, // 259: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse + 131, // 260: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse + 131, // 261: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse + 136, // 262: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse + 138, // 263: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse + 131, // 264: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse + 142, // 265: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse + 131, // 266: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse + 131, // 267: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse + 113, // 268: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 269: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 270: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse + 116, // 271: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 147, // 272: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 147, // 273: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 131, // 274: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse + 152, // 275: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse + 154, // 276: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob + 156, // 277: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs + 156, // 278: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs + 154, // 279: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob + 131, // 280: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse + 131, // 281: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse + 59, // 282: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse + 161, // 283: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse + 167, // 284: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse + 131, // 285: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse + 131, // 286: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse + 131, // 287: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse + 131, // 288: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse + 170, // 289: gctrpc.GoCryptoTrader.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse + 195, // [195:290] is the sub-list for method output_type + 100, // [100:195] is the sub-list for method input_type + 100, // [100:100] is the sub-list for extension type_name + 100, // [100:100] is the sub-list for extension extendee + 0, // [0:100] is the sub-list for field type_name } func init() { file_rpc_proto_init() } @@ -15538,7 +15838,43 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[179].Exporter = func(v interface{}, i int) interface{} { + file_rpc_proto_msgTypes[169].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFuturesPositionsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[170].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFuturesPositionsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[171].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FuturePosition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[182].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelBatchOrdersResponse_Orders); i { case 0: return &v.state @@ -15550,7 +15886,7 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[181].Exporter = func(v interface{}, i int) interface{} { + file_rpc_proto_msgTypes[184].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelAllOrdersResponse_Orders); i { case 0: return &v.state @@ -15569,7 +15905,7 @@ func file_rpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rpc_proto_rawDesc, NumEnums: 0, - NumMessages: 185, + NumMessages: 188, NumExtensions: 0, NumServices: 1, }, diff --git a/gctrpc/rpc.pb.gw.go b/gctrpc/rpc.pb.gw.go index 8a046caac53..a1fee1e301d 100644 --- a/gctrpc/rpc.pb.gw.go +++ b/gctrpc/rpc.pb.gw.go @@ -3023,6 +3023,42 @@ func local_request_GoCryptoTrader_CurrencyStateTradingPair_0(ctx context.Context } +var ( + filter_GoCryptoTrader_GetFuturesPositions_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_GetFuturesPositions_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetFuturesPositionsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetFuturesPositions_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetFuturesPositions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_GetFuturesPositions_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetFuturesPositionsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetFuturesPositions_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetFuturesPositions(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterGoCryptoTraderHandlerServer registers the http handlers for service GoCryptoTrader to "mux". // UnaryRPC :call GoCryptoTraderServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -5095,6 +5131,29 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetFuturesPositions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetFuturesPositions", runtime.WithHTTPPathPattern("/v1/getfuturespositions")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_GetFuturesPositions_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetFuturesPositions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7016,6 +7075,26 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetFuturesPositions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetFuturesPositions", runtime.WithHTTPPathPattern("/v1/getfuturespositions")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_GetFuturesPositions_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetFuturesPositions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7207,6 +7286,8 @@ var ( pattern_GoCryptoTrader_CurrencyStateWithdraw_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "currencystatewithdraw"}, "")) pattern_GoCryptoTrader_CurrencyStateTradingPair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "currencystatetradingpair"}, "")) + + pattern_GoCryptoTrader_GetFuturesPositions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getfuturespositions"}, "")) ) var ( @@ -7397,4 +7478,6 @@ var ( forward_GoCryptoTrader_CurrencyStateWithdraw_0 = runtime.ForwardResponseMessage forward_GoCryptoTrader_CurrencyStateTradingPair_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_GetFuturesPositions_0 = runtime.ForwardResponseMessage ) diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 950b34ba197..0daab075d47 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1023,6 +1023,32 @@ message CurrencyState { bool trading_enabled = 5; } +message GetFuturesPositionsRequest { + string exchange =1; + string asset = 2; + CurrencyPair pair = 3; + string start_date = 4; + string end_date = 5; + int64 status = 6; + int64 positionLimit = 7; +} + + +message GetFuturesPositionsResponse { + int64 totalOrders = 1; + repeated FuturePosition positions = 2; +} + +message FuturePosition { + string status = 1; + string currentDirection = 2; + string unrealisedPNL = 3; + string realisedPNL = 4; + string openingDate = 5; + string closingDate = 6; + repeated OrderDetails orders = 7; +} + service GoCryptoTrader { rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) { option (google.api.http) = { @@ -1612,4 +1638,9 @@ service GoCryptoTrader { get: "/v1/currencystatetradingpair" }; } + rpc GetFuturesPositions (GetFuturesPositionsRequest) returns (GetFuturesPositionsResponse) { + option (google.api.http) = { + get: "/v1/getfuturespositions" + }; + } } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index e35e6a77a3c..f4ff1b3980f 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1705,6 +1705,86 @@ ] } }, + "/v1/getfuturespositions": { + "get": { + "operationId": "GoCryptoTrader_GetFuturesPositions", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetFuturesPositionsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "asset", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.delimiter", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.base", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.quote", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "startDate", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "endDate", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "positionLimit", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/gethistoriccandles": { "get": { "operationId": "GoCryptoTrader_GetHistoricCandles", @@ -3805,6 +3885,35 @@ } } }, + "gctrpcFuturePosition": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "currentDirection": { + "type": "string" + }, + "unrealisedPNL": { + "type": "string" + }, + "realisedPNL": { + "type": "string" + }, + "openingDate": { + "type": "string" + }, + "closingDate": { + "type": "string" + }, + "orders": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcOrderDetails" + } + } + } + }, "gctrpcGCTScript": { "type": "object", "properties": { @@ -4181,6 +4290,21 @@ } } }, + "gctrpcGetFuturesPositionsResponse": { + "type": "object", + "properties": { + "totalOrders": { + "type": "string", + "format": "int64" + }, + "positions": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcFuturePosition" + } + } + } + }, "gctrpcGetHistoricCandlesResponse": { "type": "object", "properties": { diff --git a/gctrpc/rpc_grpc.pb.go b/gctrpc/rpc_grpc.pb.go index 3f0e90f383c..3f7906ad55b 100644 --- a/gctrpc/rpc_grpc.pb.go +++ b/gctrpc/rpc_grpc.pb.go @@ -112,6 +112,7 @@ type GoCryptoTraderClient interface { CurrencyStateDeposit(ctx context.Context, in *CurrencyStateDepositRequest, opts ...grpc.CallOption) (*GenericResponse, error) CurrencyStateWithdraw(ctx context.Context, in *CurrencyStateWithdrawRequest, opts ...grpc.CallOption) (*GenericResponse, error) CurrencyStateTradingPair(ctx context.Context, in *CurrencyStateTradingPairRequest, opts ...grpc.CallOption) (*GenericResponse, error) + GetFuturesPositions(ctx context.Context, in *GetFuturesPositionsRequest, opts ...grpc.CallOption) (*GetFuturesPositionsResponse, error) } type goCryptoTraderClient struct { @@ -1106,6 +1107,15 @@ func (c *goCryptoTraderClient) CurrencyStateTradingPair(ctx context.Context, in return out, nil } +func (c *goCryptoTraderClient) GetFuturesPositions(ctx context.Context, in *GetFuturesPositionsRequest, opts ...grpc.CallOption) (*GetFuturesPositionsResponse, error) { + out := new(GetFuturesPositionsResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GetFuturesPositions", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GoCryptoTraderServer is the server API for GoCryptoTrader service. // All implementations must embed UnimplementedGoCryptoTraderServer // for forward compatibility @@ -1204,6 +1214,7 @@ type GoCryptoTraderServer interface { CurrencyStateDeposit(context.Context, *CurrencyStateDepositRequest) (*GenericResponse, error) CurrencyStateWithdraw(context.Context, *CurrencyStateWithdrawRequest) (*GenericResponse, error) CurrencyStateTradingPair(context.Context, *CurrencyStateTradingPairRequest) (*GenericResponse, error) + GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) mustEmbedUnimplementedGoCryptoTraderServer() } @@ -1493,6 +1504,9 @@ func (UnimplementedGoCryptoTraderServer) CurrencyStateWithdraw(context.Context, func (UnimplementedGoCryptoTraderServer) CurrencyStateTradingPair(context.Context, *CurrencyStateTradingPairRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CurrencyStateTradingPair not implemented") } +func (UnimplementedGoCryptoTraderServer) GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetFuturesPositions not implemented") +} func (UnimplementedGoCryptoTraderServer) mustEmbedUnimplementedGoCryptoTraderServer() {} // UnsafeGoCryptoTraderServer may be embedded to opt out of forward compatibility for this service. @@ -3216,6 +3230,24 @@ func _GoCryptoTrader_CurrencyStateTradingPair_Handler(srv interface{}, ctx conte return interceptor(ctx, in, info, handler) } +func _GoCryptoTrader_GetFuturesPositions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetFuturesPositionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).GetFuturesPositions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/GetFuturesPositions", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).GetFuturesPositions(ctx, req.(*GetFuturesPositionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GoCryptoTrader_ServiceDesc is the grpc.ServiceDesc for GoCryptoTrader service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -3575,6 +3607,10 @@ var GoCryptoTrader_ServiceDesc = grpc.ServiceDesc{ MethodName: "CurrencyStateTradingPair", Handler: _GoCryptoTrader_CurrencyStateTradingPair_Handler, }, + { + MethodName: "GetFuturesPositions", + Handler: _GoCryptoTrader_GetFuturesPositions_Handler, + }, }, Streams: []grpc.StreamDesc{ { From 1361a8adeaa5e708ab6ca0128596e624da7d2b0b Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 29 Dec 2021 16:27:38 +1100 Subject: [PATCH 047/171] grpc implementations --- cmd/gctcli/commands.go | 184 ++++++++++++++++++++++++++++++- cmd/gctcli/main.go | 1 + engine/rpcserver.go | 44 +++++++- exchanges/exchange.go | 6 +- exchanges/ftx/ftx.go | 10 +- exchanges/ftx/ftx_wrapper.go | 34 +++++- exchanges/interfaces.go | 1 + exchanges/order/futures.go | 139 ++++++++++------------- exchanges/order/futures_test.go | 10 +- exchanges/order/futures_types.go | 18 ++- gctrpc/rpc.pb.go | 8 +- gctrpc/rpc.proto | 2 +- gctrpc/rpc.swagger.json | 3 +- 13 files changed, 352 insertions(+), 108 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 50b850370fa..4d608a4c86e 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -17,7 +17,7 @@ import ( "github.com/urfave/cli/v2" ) -var startTime, endTime, order string +var startTime, endTime, o string var limit int var getInfoCommand = &cli.Command{ @@ -3742,7 +3742,7 @@ var getAuditEventCommand = &cli.Command{ Aliases: []string{"o"}, Usage: "order results by ascending/descending", Value: "asc", - Destination: &order, + Destination: &o, }, &cli.IntFlag{ Name: "limit", @@ -3769,7 +3769,7 @@ func getAuditEvent(c *cli.Context) error { if !c.IsSet("order") { if c.Args().Get(2) != "" { - order = c.Args().Get(2) + o = c.Args().Get(2) } } @@ -3810,7 +3810,7 @@ func getAuditEvent(c *cli.Context) error { StartDate: negateLocalOffset(s), EndDate: negateLocalOffset(e), Limit: int32(limit), - OrderBy: order, + OrderBy: o, }) if err != nil { @@ -4737,3 +4737,179 @@ func negateLocalOffset(t time.Time) string { return t.In(loc).Format(common.SimpleTimeFormat) } + +func getFuturesPositions(c *cli.Context) error { + /* + Exchange + Asset + Pair + StartDate + EndDate + Status + PositionLimit + */ + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, "getfuturesposition") + } + + var exchangeName string + if c.IsSet("exchange") { + exchangeName = c.String("exchange") + } else { + exchangeName = c.Args().First() + } + + var assetType string + if c.IsSet("asset") { + assetType = c.String("asset") + } else { + assetType = c.Args().Get(1) + } + + if !validAsset(assetType) { + return errInvalidAsset + } + + var currencyPair string + if c.IsSet("pair") { + currencyPair = c.String("pair") + } else { + currencyPair = c.Args().Get(2) + } + if !validPair(currencyPair) { + return errInvalidPair + } + + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + fmt.Printf("%v", currencyPair) + fmt.Printf("%v", p) + + if !c.IsSet("start") { + if c.Args().Get(3) != "" { + startTime = c.Args().Get(3) + } + } + + if !c.IsSet("end") { + if c.Args().Get(4) != "" { + endTime = c.Args().Get(4) + } + } + var limit64 int64 + if c.IsSet("limit") { + limit64 = c.Int64("limit") + } else if c.Args().Get(5) != "" { + limit64, err = strconv.ParseInt(c.Args().Get(5), 10, 64) + if err != nil { + return err + } + } + + var status string + if c.IsSet("status") { + status = c.String("status") + } else if c.Args().Get(6) != "" { + status = c.Args().Get(6) + } + if !strings.EqualFold(status, "any") && + !strings.EqualFold(status, "open") && + !strings.EqualFold(status, "closed") && + status != "" { + return errors.New("unrecognised status") + } + + var s, e time.Time + s, err = time.Parse(common.SimpleTimeFormat, startTime) + if err != nil { + return fmt.Errorf("invalid time format for start: %v", err) + } + e, err = time.Parse(common.SimpleTimeFormat, endTime) + if err != nil { + return fmt.Errorf("invalid time format for end: %v", err) + } + + if e.Before(s) { + return errors.New("start cannot be after end") + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.GetFuturesPositions(c.Context, + &gctrpc.GetFuturesPositionsRequest{ + Exchange: exchangeName, + Asset: assetType, + Pair: &gctrpc.CurrencyPair{ + Delimiter: p.Delimiter, + Base: p.Base.String(), + Quote: p.Quote.String(), + }, + StartDate: negateLocalOffset(s), + EndDate: negateLocalOffset(e), + Status: status, + PositionLimit: limit64, + }) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var getFuturesPositionsCommand = &cli.Command{ + Name: "getfuturesposition", + Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened", + ArgsUsage: " ", + Action: getFuturesPositions, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "exchange", + Aliases: []string{"e"}, + Usage: "the exchange to find the missing candles", + }, + &cli.StringFlag{ + Name: "asset", + Aliases: []string{"a"}, + Usage: "the asset type of the currency pair", + }, + &cli.StringFlag{ + Name: "pair", + Aliases: []string{"p"}, + Usage: "the currency pair", + }, + &cli.StringFlag{ + Name: "start", + Aliases: []string{"sd"}, + Usage: " rounded down to the nearest hour", + Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat), + Destination: &startTime, + }, + &cli.StringFlag{ + Name: "end", + Aliases: []string{"ed"}, + Usage: " rounded down to the nearest hour", + Value: time.Now().Truncate(time.Hour).Format(common.SimpleTimeFormat), + Destination: &endTime, + }, + &cli.Int64Flag{ + Name: "limit", + Aliases: []string{"l"}, + Usage: "the number of positions (not orders) to return", + Value: 86400, + }, + &cli.StringFlag{ + Name: "status", + Aliases: []string{"s"}, + Usage: "limit return to position statuses - open, closed, any", + Value: "ANY", + }, + }, +} diff --git a/cmd/gctcli/main.go b/cmd/gctcli/main.go index b76f468ea62..a6a09f9aaae 100644 --- a/cmd/gctcli/main.go +++ b/cmd/gctcli/main.go @@ -163,6 +163,7 @@ func main() { tradeCommand, dataHistoryCommands, currencyStateManagementCommand, + getFuturesPositionsCommand, } ctx, cancel := context.WithCancel(context.Background()) diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 140069a2389..dc765ff62bc 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4113,8 +4113,7 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP if err != nil { return nil, err } - - cp, err := currency.NewPairFromString(r.Pair.String()) + cp, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) if err != nil { return nil, err } @@ -4124,10 +4123,47 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP if err != nil { return nil, err } + var start, end time.Time + if r.StartDate != "" { + start, err = time.Parse(common.SimpleTimeFormat, r.StartDate) + if err != nil { + return nil, err + } + } + if r.EndDate != "" { + end, err = time.Parse(common.SimpleTimeFormat, r.EndDate) + if err != nil { + return nil, err + } + } + err = common.StartEndTimeCheck(start, end) + if err != nil { + return nil, err + } pos, err := s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) if err != nil { - return nil, err + if errors.Is(err, order.ErrPositionsNotLoadedForExchange) || + errors.Is(err, order.ErrPositionsNotLoadedForAsset) || + errors.Is(err, order.ErrPositionsNotLoadedForPair) { + var orders []order.Detail + orders, err = exch.GetFuturesPositions(a, cp, start, end) + if err != nil { + return nil, err + } + for i := range orders { + err = s.OrderManager.orderStore.futuresPositionTrackers.TrackNewOrder(&orders[i]) + if err != nil { + return nil, err + } + } + pos, err = s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) + if err != nil { + return nil, err + } + } else { + return nil, err + } } response := &gctrpc.GetFuturesPositionsResponse{} for i := range pos { @@ -4168,7 +4204,7 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP QuoteCurrency: stats.Orders[j].Pair.Quote.String(), AssetType: stats.Orders[j].AssetType.String(), OrderSide: stats.Orders[j].Side.String(), - OrderType: stats.Orders[j].Type, + OrderType: stats.Orders[j].Type.String(), CreationTime: stats.Orders[j].Date.Unix(), UpdateTime: stats.Orders[j].LastUpdated.Unix(), Status: stats.Orders[j].Status.String(), diff --git a/exchanges/exchange.go b/exchanges/exchange.go index f572248252c..9b8f287ac1b 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1467,7 +1467,7 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ // It will also determine whether the position is considered to be liquidated // for live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset -func (b *Base) CalculatePNL(*order.PNLCalculator) (*order.PNLResult, error) { +func (b *Base) CalculatePNL(*order.PNLCalculatorRequest) (*order.PNLResult, error) { return nil, common.ErrNotYetImplemented } @@ -1483,3 +1483,7 @@ func (b *Base) ScaleCollateral(*order.CollateralCalculator) (decimal.Decimal, er func (b *Base) CalculateTotalCollateral([]order.CollateralCalculator) (decimal.Decimal, error) { return decimal.Zero, common.ErrNotYetImplemented } + +func (b *Base) GetFuturesPositions(asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) { + return nil, common.ErrNotYetImplemented +} diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index a2d971abe02..7a36059315c 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -840,13 +840,17 @@ func (f *FTX) DeleteTriggerOrder(ctx context.Context, orderID string) (string, e } // GetFills gets fills' data -func (f *FTX) GetFills(ctx context.Context, market, limit string, startTime, endTime time.Time) ([]FillsData, error) { +func (f *FTX) GetFills(ctx context.Context, market currency.Pair, limit string, startTime, endTime time.Time) ([]FillsData, error) { resp := struct { Data []FillsData `json:"result"` }{} params := url.Values{} - if market != "" { - params.Set("market", market) + if !market.IsEmpty() { + fp, err := f.FormatExchangeCurrency(market, asset.Futures) + if err != nil { + return nil, err + } + params.Set("market", fp.String()) } if limit != "" { params.Set("limit", limit) diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 0cf897ec7da..042550d9208 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1280,7 +1280,7 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur // amount // CalculatePNL uses a high-tech algorithm to calculate your pnl -func (f *FTX) CalculatePNL(pnl *order.PNLCalculator) (*order.PNLResult, error) { +func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { if pnl.ExchangeBasedCalculation == nil { return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } @@ -1355,3 +1355,35 @@ func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalcul } return result, nil } + +func (f *FTX) GetFuturesPositions(a asset.Item, cp currency.Pair, start, end time.Time) ([]order.Detail, error) { + if !a.IsFutures() { + return nil, fmt.Errorf("%w futures asset type only", common.ErrFunctionNotSupported) + } + fills, err := f.GetFills(context.Background(), cp, "200", start, end) + if err != nil { + return nil, err + } + var resp []order.Detail + var side order.Side + for i := range fills { + price := fills[i].Price + side, err = order.StringToOrderSide(fills[i].Side) + if err != nil { + return nil, err + } + resp = append(resp, order.Detail{ + Side: side, + Pair: cp, + ID: fmt.Sprintf("%v", fills[i].ID), + Price: price, + Amount: fills[i].Size, + AssetType: a, + Exchange: f.Name, + Fee: fills[i].Fee, + Date: fills[i].Time, + }) + } + + return resp, nil +} diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index bab9d09cb8c..d80cd7fa5c5 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -84,6 +84,7 @@ type IBotExchange interface { order.PNLCalculation order.CollateralManagement + GetFuturesPositions(asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) GetWebsocket() (*stream.Websocket, error) IsWebsocketEnabled() bool diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 2f9b4aa8bde..f52826a72f9 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -62,21 +62,19 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite return nil, common.ErrNilPointer } c.m.Lock() + defer c.m.Unlock() exchM, ok := c.positionTrackerControllers[exch] if !ok { - return nil, errExchangeNotFound + return nil, ErrPositionsNotLoadedForExchange } itemM, ok := exchM[item] if !ok { - c.m.Unlock() - return nil, errAssetNotFound + return nil, ErrPositionsNotLoadedForAsset } multiPositionTracker, ok := itemM[pair] if !ok { - c.m.Unlock() - return nil, currency.ErrPairNotFound + return nil, ErrPositionsNotLoadedForPair } - c.m.Unlock() return multiPositionTracker.GetPositions(), nil } @@ -132,25 +130,22 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { return ErrSubmissionIsNil } e.m.Lock() - e.m.Unlock() + defer e.m.Unlock() if d.AssetType != e.asset { return errAssetMismatch } if tracker, ok := e.orderPositions[d.ID]; ok { // this has already been associated // update the tracker - e.m.Unlock() return tracker.TrackNewOrder(d) } if len(e.positions) > 0 { for i := range e.positions { if e.positions[i].status == Open && i != len(e.positions)-1 { - e.m.Unlock() return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) } } if e.positions[len(e.positions)-1].status == Open { - e.m.Unlock() err := e.positions[len(e.positions)-1].TrackNewOrder(d) if err != nil && !errors.Is(err, errPositionClosed) { return err @@ -169,12 +164,9 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { } tracker, err := e.SetupPositionTracker(setup) if err != nil { - e.m.Unlock() return err } e.positions = append(e.positions, tracker) - - e.m.Unlock() err = tracker.TrackNewOrder(d) if err != nil { return err @@ -215,7 +207,7 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) } if !e.useExchangePNLCalculations { // use position tracker's pnl calculation by default - resp.PNLCalculation = resp + resp.PNLCalculation = &PNLCalculator{} } else { resp.PNLCalculation = e.exchangePNLCalculation } @@ -254,7 +246,7 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro defer func() { p.latestPrice = decimal.NewFromFloat(currentPrice) }() - pnl, err := p.PNLCalculation.CalculatePNL(&PNLCalculator{ + pnl, err := p.PNLCalculation.CalculatePNL(&PNLCalculatorRequest{ TimeBasedCalculation: &TimeBasedCalculation{ Time: t, CurrentPrice: currentPrice, @@ -263,17 +255,22 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro if err != nil { return err } - return p.upsertPNLEntry(&PNLResult{ + p.m.Lock() + defer p.m.Unlock() + p.pnlHistory, err = p.upsertPNLEntry(p.pnlHistory, &PNLResult{ Time: pnl.Time, UnrealisedPNL: pnl.UnrealisedPNL, RealisedPNLBeforeFees: pnl.RealisedPNLBeforeFees, }) + return err } // GetRealisedPNL returns the realised pnl if the order // is closed func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { - return p.calculateRealisedPNL() + p.m.Lock() + defer p.m.Unlock() + return p.calculateRealisedPNL(p.pnlHistory) } // GetLatestPNLSnapshot takes the latest pnl history value @@ -376,11 +373,9 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if len(p.pnlHistory) != 0 { cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price.InexactFloat64() } - p.m.Unlock() - result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{ExchangeBasedCalculation: cal}) + result, err = p.PNLCalculation.CalculatePNL(&PNLCalculatorRequest{ExchangeBasedCalculation: cal}) } else { - p.m.Unlock() - result, err = p.PNLCalculation.CalculatePNL(&PNLCalculator{OrderBasedCalculation: d}) + result, err = p.PNLCalculation.CalculatePNL(&PNLCalculatorRequest{OrderBasedCalculation: d}) } if err != nil { if !errors.Is(err, ErrPositionLiquidated) { @@ -390,9 +385,9 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { result.RealisedPNLBeforeFees = decimal.Zero p.status = Closed } - upsertErr := p.upsertPNLEntry(result) - if upsertErr != nil { - return upsertErr + p.pnlHistory, err = p.upsertPNLEntry(p.pnlHistory, result) + if err != nil { + return err } p.unrealisedPNL = result.UnrealisedPNL @@ -411,7 +406,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) - p.realisedPNL = p.calculateRealisedPNL() + p.realisedPNL = p.calculateRealisedPNL(p.pnlHistory) } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short @@ -425,16 +420,13 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { // CalculatePNL this is a localised generic way of calculating open // positions' worth, it is an implementation of the PNLCalculation interface -func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) { - // re work this to have no dependency on "p", this way locking has no impact here and its less messy - if p == nil { - return nil, common.ErrNilPointer - } +// +// do not use any properties of p, use calc, otherwise there will be +// sync issues +func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, error) { if calc == nil { return nil, ErrNilPNLCalculator } - p.m.Lock() - defer p.m.Unlock() result := &PNLResult{} var price, amount decimal.Decimal var err error @@ -442,12 +434,13 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) fee := decimal.NewFromFloat(calc.OrderBasedCalculation.Fee) - if (p.currentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || p.currentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && - p.exposure.LessThan(amount) { + // seperate this out for before here, to do this twice + if (calc.CurrentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || calc.CurrentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && + calc.Exposure.LessThan(amount) { // latest order swaps directions! - first := amount.Sub(p.exposure) - second := p.exposure.Sub(amount).Abs() + first := amount.Sub(calc.Exposure) + second := calc.Exposure.Sub(amount).Abs() fee = fee.Div(decimal.NewFromInt(2)) result, err = createPNLResult( calc.OrderBasedCalculation.Date, @@ -455,13 +448,13 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) first, price, fee, - p.openingDirection, - p.currentDirection, - p.pnlHistory) + calc.OpeningDirection, + calc.CurrentDirection, + calc.PNLHistory) if err != nil { return nil, err } - err = p.upsertPNLEntry(result) + p.pnlHistory, err = p.upsertPNLEntry(calc.PNLHistory, result) if err != nil { return nil, err } @@ -471,22 +464,22 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) } else if calc.OrderBasedCalculation.Side.IsShort() { calc.OrderBasedCalculation.Side = Long } - if p.openingDirection.IsLong() { - p.openingDirection = Short - } else if p.openingDirection.IsShort() { - p.openingDirection = Long + if calc.OpeningDirection.IsLong() { + calc.OpeningDirection = Short + } else if calc.OpeningDirection.IsShort() { + calc.OpeningDirection = Long } - p.entryPrice = price + calc.EntryPrice = price result, err = createPNLResult( calc.OrderBasedCalculation.Date.Add(1), calc.OrderBasedCalculation.Side, second, price, fee, - p.openingDirection, - p.currentDirection, - p.pnlHistory) + calc.OpeningDirection, + calc.CurrentDirection, + calc.PNLHistory) if err != nil { return nil, err } @@ -498,9 +491,9 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) amount, price, fee, - p.openingDirection, - p.currentDirection, - p.pnlHistory) + calc.OpeningDirection, + calc.CurrentDirection, + calc.PNLHistory) if err != nil { return nil, err } @@ -509,12 +502,12 @@ func (p *PositionTracker) CalculatePNL(calc *PNLCalculator) (*PNLResult, error) } else if calc.TimeBasedCalculation != nil { result.Time = calc.TimeBasedCalculation.Time price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) - diff := price.Sub(p.entryPrice) - result.UnrealisedPNL = p.exposure.Mul(diff) + diff := price.Sub(calc.EntryPrice) + result.UnrealisedPNL = calc.Exposure.Mul(diff) result.Price = price - if len(p.pnlHistory) > 0 { - result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees - result.Exposure = p.pnlHistory[len(p.pnlHistory)-1].Exposure + if len(calc.PNLHistory) > 0 { + result.RealisedPNLBeforeFees = calc.PNLHistory[len(calc.PNLHistory)-1].RealisedPNLBeforeFees + result.Exposure = calc.PNLHistory[len(calc.PNLHistory)-1].Exposure } return result, nil } @@ -578,16 +571,11 @@ func createPNLResult(t time.Time, side Side, amount, price, fee decimal.Decimal, return response, nil } -func (p *PositionTracker) calculateRealisedPNL() decimal.Decimal { - if p == nil { - return decimal.Zero - } - p.m.Lock() - defer p.m.Unlock() +func (p *PositionTracker) calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal - for i := range p.pnlHistory { - realisedPNL = realisedPNL.Add(p.pnlHistory[i].RealisedPNLBeforeFees) - totalFees = totalFees.Add(p.pnlHistory[i].Fee) + for i := range pnlHistory { + realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) + totalFees = totalFees.Add(pnlHistory[i].Fee) } if realisedPNL.IsZero() { return decimal.Zero @@ -598,21 +586,16 @@ func (p *PositionTracker) calculateRealisedPNL() decimal.Decimal { // upsertPNLEntry upserts an entry to PNLHistory field // with some basic checks -func (p *PositionTracker) upsertPNLEntry(entry *PNLResult) error { - if p == nil { - return common.ErrNilPointer - } - p.m.Lock() - defer p.m.Unlock() +func (p *PositionTracker) upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, error) { if entry.Time.IsZero() { - return errTimeUnset + return nil, errTimeUnset } - for i := range p.pnlHistory { - if entry.Time.Equal(p.pnlHistory[i].Time) { - p.pnlHistory[i] = *entry - return nil + for i := range pnlHistory { + if entry.Time.Equal(pnlHistory[i].Time) { + pnlHistory[i] = *entry + return pnlHistory, nil } } - p.pnlHistory = append(p.pnlHistory, *entry) - return nil + pnlHistory = append(pnlHistory, *entry) + return pnlHistory, nil } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 2c556d9c13a..2a448d61444 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -15,7 +15,7 @@ type FakePNL struct { result *PNLResult } -func (f *FakePNL) CalculatePNL(*PNLCalculator) (*PNLResult, error) { +func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { if f.err != nil { return nil, f.err } @@ -454,12 +454,12 @@ func TestCalculatePNL(t *testing.T) { t.Error(err) } - _, err = pt.CalculatePNL(&PNLCalculator{}) + _, err = pt.CalculatePNL(&PNLCalculatorRequest{}) if !errors.Is(err, errMissingPNLCalculationFunctions) { t.Error(err) } tt := time.Now() - result, err := pt.CalculatePNL(&PNLCalculator{ + result, err := pt.CalculatePNL(&PNLCalculatorRequest{ TimeBasedCalculation: &TimeBasedCalculation{ Time: tt, CurrentPrice: 1337, @@ -474,7 +474,7 @@ func TestCalculatePNL(t *testing.T) { pt.status = Open pt.currentDirection = Long - result, err = pt.CalculatePNL(&PNLCalculator{ + result, err = pt.CalculatePNL(&PNLCalculatorRequest{ OrderBasedCalculation: &Detail{ Date: tt, Price: 1337, @@ -491,7 +491,7 @@ func TestCalculatePNL(t *testing.T) { } pt.exposure = decimal.NewFromInt(5) - result, err = pt.CalculatePNL(&PNLCalculator{ + result, err = pt.CalculatePNL(&PNLCalculatorRequest{ OrderBasedCalculation: &Detail{ Date: tt, Price: 1337, diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 197a0240a6d..e6d56b08531 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -25,8 +25,9 @@ var ( errNilOrder = errors.New("nil order received") errNoPNLHistory = errors.New("no pnl history") errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL, order is not open") - errExchangeNotFound = errors.New("exchange not found") - errAssetNotFound = errors.New("asset not found") + ErrPositionsNotLoadedForExchange = errors.New("no positions loaded for exchange") + ErrPositionsNotLoadedForAsset = errors.New("no positions loaded for asset") + ErrPositionsNotLoadedForPair = errors.New("no positions loaded for pair") // ErrNilPNLCalculator is raised when pnl calculation is requested for // an exchange, but the fields are not set properly @@ -39,7 +40,7 @@ var ( // PNLCalculation is an interface to allow multiple // ways of calculating PNL to be used for futures positions type PNLCalculation interface { - CalculatePNL(*PNLCalculator) (*PNLResult, error) + CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) } // CollateralManagement is an interface that allows @@ -147,9 +148,16 @@ type CollateralCalculator struct { IsLiquidating bool } -// PNLCalculator is used to calculate PNL values +type PNLCalculator struct{} + +// PNLCalculatorRequest is used to calculate PNL values // for an open position -type PNLCalculator struct { +type PNLCalculatorRequest struct { + OpeningDirection Side + CurrentDirection Side + Exposure decimal.Decimal + EntryPrice decimal.Decimal + PNLHistory []PNLResult OrderBasedCalculation *Detail TimeBasedCalculation *TimeBasedCalculation ExchangeBasedCalculation *ExchangeBasedCalculation diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index ae010783861..f378ce15172 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -10775,7 +10775,7 @@ type GetFuturesPositionsRequest struct { Pair *CurrencyPair `protobuf:"bytes,3,opt,name=pair,proto3" json:"pair,omitempty"` StartDate string `protobuf:"bytes,4,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` - Status int64 `protobuf:"varint,6,opt,name=status,proto3" json:"status,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` } @@ -10846,11 +10846,11 @@ func (x *GetFuturesPositionsRequest) GetEndDate() string { return "" } -func (x *GetFuturesPositionsRequest) GetStatus() int64 { +func (x *GetFuturesPositionsRequest) GetStatus() string { if x != nil { return x.Status } - return 0 + return "" } func (x *GetFuturesPositionsRequest) GetPositionLimit() int64 { @@ -12562,7 +12562,7 @@ var file_rpc_proto_rawDesc = []byte{ 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x75, diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 0daab075d47..88ec7207768 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1029,7 +1029,7 @@ message GetFuturesPositionsRequest { CurrencyPair pair = 3; string start_date = 4; string end_date = 5; - int64 status = 6; + string status = 6; int64 positionLimit = 7; } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index f4ff1b3980f..2125bd9f014 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1769,8 +1769,7 @@ "name": "status", "in": "query", "required": false, - "type": "string", - "format": "int64" + "type": "string" }, { "name": "positionLimit", From af3faf1ba11d4fc1bcb9fb9c26688e0db23ae1e3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 30 Dec 2021 15:42:56 +1100 Subject: [PATCH 048/171] Gracefully handles rpc function --- cmd/gctcli/commands.go | 36 +- engine/rpcserver.go | 45 +- exchanges/ftx/ftx_wrapper.go | 14 +- exchanges/order/futures.go | 242 +++-- exchanges/order/futures_test.go | 2 +- exchanges/order/futures_types.go | 67 +- gctrpc/rpc.pb.go | 1473 +++++++++++++++--------------- gctrpc/rpc.proto | 6 +- gctrpc/rpc.swagger.json | 18 + 9 files changed, 984 insertions(+), 919 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 4d608a4c86e..cd9d066903d 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4784,8 +4784,6 @@ func getFuturesPositions(c *cli.Context) error { if err != nil { return err } - fmt.Printf("%v", currencyPair) - fmt.Printf("%v", p) if !c.IsSet("start") { if c.Args().Get(3) != "" { @@ -4798,14 +4796,15 @@ func getFuturesPositions(c *cli.Context) error { endTime = c.Args().Get(4) } } - var limit64 int64 if c.IsSet("limit") { - limit64 = c.Int64("limit") + limit = c.Int("limit") } else if c.Args().Get(5) != "" { + var limit64 int64 limit64, err = strconv.ParseInt(c.Args().Get(5), 10, 64) if err != nil { return err } + limit = int(limit64) } var status string @@ -4821,6 +4820,16 @@ func getFuturesPositions(c *cli.Context) error { return errors.New("unrecognised status") } + var verbose bool + if c.IsSet("verbose") { + verbose = c.Bool("verbose") + } else if c.Args().Get(6) != "" { + verbose, err = strconv.ParseBool(c.Args().Get(7)) + if err != nil { + return err + } + } + var s, e time.Time s, err = time.Parse(common.SimpleTimeFormat, startTime) if err != nil { @@ -4854,7 +4863,8 @@ func getFuturesPositions(c *cli.Context) error { StartDate: negateLocalOffset(s), EndDate: negateLocalOffset(e), Status: status, - PositionLimit: limit64, + PositionLimit: int64(limit), + Verbose: verbose, }) if err != nil { return err @@ -4899,11 +4909,12 @@ var getFuturesPositionsCommand = &cli.Command{ Value: time.Now().Truncate(time.Hour).Format(common.SimpleTimeFormat), Destination: &endTime, }, - &cli.Int64Flag{ - Name: "limit", - Aliases: []string{"l"}, - Usage: "the number of positions (not orders) to return", - Value: 86400, + &cli.IntFlag{ + Name: "limit", + Aliases: []string{"l"}, + Usage: "the number of positions (not orders) to return", + Value: 86400, + Destination: &limit, }, &cli.StringFlag{ Name: "status", @@ -4911,5 +4922,10 @@ var getFuturesPositionsCommand = &cli.Command{ Usage: "limit return to position statuses - open, closed, any", Value: "ANY", }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "includes all orders in the response", + }, }, } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index dc765ff62bc..941fbc85048 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "path/filepath" + "sort" "strconv" "strings" "time" @@ -4141,32 +4142,30 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP return nil, err } - pos, err := s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) + orders, err := exch.GetFuturesPositions(a, cp, start, end) if err != nil { - if errors.Is(err, order.ErrPositionsNotLoadedForExchange) || - errors.Is(err, order.ErrPositionsNotLoadedForAsset) || - errors.Is(err, order.ErrPositionsNotLoadedForPair) { - var orders []order.Detail - orders, err = exch.GetFuturesPositions(a, cp, start, end) - if err != nil { - return nil, err - } - for i := range orders { - err = s.OrderManager.orderStore.futuresPositionTrackers.TrackNewOrder(&orders[i]) - if err != nil { - return nil, err - } - } - pos, err = s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) - if err != nil { + return nil, err + } + sort.Slice(orders, func(i, j int) bool { + return orders[i].Date.Before(orders[j].Date) + }) + for i := range orders { + err = s.OrderManager.orderStore.futuresPositionTrackers.TrackNewOrder(&orders[i]) + if err != nil { + if !errors.Is(err, order.ErrPositionClosed) { return nil, err } - } else { - return nil, err } } + pos, err := s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) + if err != nil { + return nil, err + } response := &gctrpc.GetFuturesPositionsResponse{} for i := range pos { + if r.PositionLimit > 0 && len(response.Positions) >= int(r.PositionLimit) { + break + } stats := pos[i].GetStats() response.TotalOrders += int64(len(stats.Orders)) details := &gctrpc.FuturePosition{ @@ -4181,6 +4180,12 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP details.ClosingDate = stats.PNLHistory[len(stats.PNLHistory)-1].Time.Format(common.SimpleTimeFormatWithTimezone) } } + response.TotalRealisedPNL += stats.RealisedPNL.InexactFloat64() + response.TotalUnrealisedPNL += stats.UnrealisedPNL.InexactFloat64() + if !r.Verbose { + response.Positions = append(response.Positions, details) + continue + } for j := range stats.Orders { var trades []*gctrpc.TradeHistory for k := range stats.Orders[j].Trades { @@ -4217,6 +4222,6 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP } response.Positions = append(response.Positions, details) } - + response.TotalPNL = response.TotalRealisedPNL + response.TotalUnrealisedPNL return response, nil } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 042550d9208..6ac688404e0 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1281,13 +1281,13 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur // CalculatePNL uses a high-tech algorithm to calculate your pnl func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { - if pnl.ExchangeBasedCalculation == nil { + if pnl == nil { return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } var result order.PNLResult - result.Time = pnl.ExchangeBasedCalculation.Time + result.Time = pnl.Time - if !pnl.ExchangeBasedCalculation.CalculateOffline { + if !pnl.CalculateOffline { info, err := f.GetAccountInfo(context.Background()) if err != nil { return nil, err @@ -1300,10 +1300,10 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e if err != nil { return nil, err } - if !pnl.ExchangeBasedCalculation.Pair.Equal(pair) { + if !pnl.Pair.Equal(pair) { continue } - if info.Positions[i].EntryPrice == pnl.ExchangeBasedCalculation.EntryPrice { + if info.Positions[i].EntryPrice == pnl.EntryPrice { result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) result.Price = decimal.NewFromFloat(info.Positions[i].Cost) @@ -1311,10 +1311,10 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e } } } - uPNL := pnl.ExchangeBasedCalculation.Amount * (pnl.ExchangeBasedCalculation.CurrentPrice - pnl.ExchangeBasedCalculation.PreviousPrice) + uPNL := pnl.Amount * (pnl.CurrentPrice - pnl.PreviousPrice) result.RealisedPNLBeforeFees = result.RealisedPNLBeforeFees.Add(result.UnrealisedPNL) result.UnrealisedPNL = decimal.NewFromFloat(uPNL) - result.Price = decimal.NewFromFloat(pnl.ExchangeBasedCalculation.CurrentPrice) + result.Price = decimal.NewFromFloat(pnl.CurrentPrice) return &result, nil } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index f52826a72f9..9fddddd8a48 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -34,27 +34,28 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return common.ErrNilPointer } c.m.Lock() + defer c.m.Unlock() if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)]; !ok { c.positionTrackerControllers[strings.ToLower(d.Exchange)] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) } if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType]; !ok { c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType] = make(map[currency.Pair]*MultiPositionTracker) } - if _, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair]; !ok { - ptc, err := SetupMultiPositionTracker(&PositionControllerSetup{ + var err error + mpt, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] + if !ok { + mpt, err = SetupMultiPositionTracker(&PositionControllerSetup{ Exchange: strings.ToLower(d.Exchange), Asset: d.AssetType, Pair: d.Pair, Underlying: d.Pair.Base, }) if err != nil { - c.m.Unlock() return err } - c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] = ptc + c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] = mpt } - c.m.Unlock() - return c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair].TrackNewOrder(d) + return mpt.TrackNewOrder(d) } func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*PositionTracker, error) { @@ -147,7 +148,7 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { } if e.positions[len(e.positions)-1].status == Open { err := e.positions[len(e.positions)-1].TrackNewOrder(d) - if err != nil && !errors.Is(err, errPositionClosed) { + if err != nil && !errors.Is(err, ErrPositionClosed) { return err } e.orderPositions[d.ID] = e.positions[len(e.positions)-1] @@ -243,25 +244,25 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro if p == nil { return common.ErrNilPointer } + p.m.Lock() defer func() { p.latestPrice = decimal.NewFromFloat(currentPrice) + p.m.Unlock() }() - pnl, err := p.PNLCalculation.CalculatePNL(&PNLCalculatorRequest{ - TimeBasedCalculation: &TimeBasedCalculation{ - Time: t, - CurrentPrice: currentPrice, - }, - }) - if err != nil { - return err + price := decimal.NewFromFloat(currentPrice) + result := &PNLResult{ + Time: t, + Price: price, + } + diff := price.Sub(p.entryPrice) + result.UnrealisedPNL = p.exposure.Mul(diff) + result.Price = price + if len(p.pnlHistory) > 0 { + result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees + result.Exposure = p.pnlHistory[len(p.pnlHistory)-1].Exposure } - p.m.Lock() - defer p.m.Unlock() - p.pnlHistory, err = p.upsertPNLEntry(p.pnlHistory, &PNLResult{ - Time: pnl.Time, - UnrealisedPNL: pnl.UnrealisedPNL, - RealisedPNLBeforeFees: pnl.RealisedPNLBeforeFees, - }) + var err error + p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) return err } @@ -291,7 +292,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.m.Lock() defer p.m.Unlock() if p.status == Closed { - return errPositionClosed + return ErrPositionClosed } if d == nil { return ErrSubmissionIsNil @@ -358,34 +359,83 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { var result *PNLResult var err error - if p.useExchangePNLCalculation { - cal := &ExchangeBasedCalculation{ - Underlying: p.underlyingAsset, - Asset: p.asset, - Side: d.Side, - Leverage: d.Leverage, - EntryPrice: p.entryPrice.InexactFloat64(), - Amount: d.Amount, - CurrentPrice: d.Price, - Pair: p.contractPair, - Time: d.Date, + cal := &PNLCalculatorRequest{ + Underlying: p.underlyingAsset, + Asset: p.asset, + OrderDirection: d.Side, + Leverage: d.Leverage, + EntryPrice: p.entryPrice.InexactFloat64(), + Amount: d.Amount, + CurrentPrice: d.Price, + Pair: p.contractPair, + Time: d.Date, + OpeningDirection: p.openingDirection, + CurrentDirection: p.currentDirection, + PNLHistory: p.pnlHistory, + Exposure: p.exposure, + Fee: decimal.NewFromFloat(d.Fee), + } + if len(p.pnlHistory) != 0 { + cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price.InexactFloat64() + } + amount := decimal.NewFromFloat(d.Amount) + if (cal.OrderDirection.IsShort() && cal.CurrentDirection.IsLong() || cal.OrderDirection.IsLong() && cal.CurrentDirection.IsShort()) && + cal.Exposure.LessThan(amount) { + // latest order swaps directions! + // split the order to calculate PNL from each direction + first := amount.Sub(cal.Exposure) + second := cal.Exposure.Sub(amount).Abs() + cal.Fee = cal.Fee.Div(decimal.NewFromInt(2)) + result, err = createPNLResult( + cal.Time, + cal.OrderDirection, + first, + decimal.NewFromFloat(d.Price), + cal.Fee, + cal.OpeningDirection, + cal.CurrentDirection, + cal.PNLHistory) + if err != nil { + return err + } + p.pnlHistory, err = upsertPNLEntry(cal.PNLHistory, result) + if err != nil { + return err + } + + if cal.OrderDirection.IsLong() { + cal.OrderDirection = Short + } else if cal.OrderDirection.IsShort() { + cal.OrderDirection = Long } - if len(p.pnlHistory) != 0 { - cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price.InexactFloat64() + if cal.OpeningDirection.IsLong() { + cal.OpeningDirection = Short + } else if cal.OpeningDirection.IsShort() { + cal.OpeningDirection = Long } - result, err = p.PNLCalculation.CalculatePNL(&PNLCalculatorRequest{ExchangeBasedCalculation: cal}) + + cal.EntryPrice = d.Price + result, err = createPNLResult( + cal.Time.Add(1), + cal.OrderDirection, + second, + decimal.NewFromFloat(d.Price), + cal.Fee, + cal.OpeningDirection, + cal.CurrentDirection, + cal.PNLHistory) } else { - result, err = p.PNLCalculation.CalculatePNL(&PNLCalculatorRequest{OrderBasedCalculation: d}) - } - if err != nil { - if !errors.Is(err, ErrPositionLiquidated) { - return err + result, err = p.PNLCalculation.CalculatePNL(cal) + if err != nil { + if !errors.Is(err, ErrPositionLiquidated) { + return err + } + result.UnrealisedPNL = decimal.Zero + result.RealisedPNLBeforeFees = decimal.Zero + p.status = Closed } - result.UnrealisedPNL = decimal.Zero - result.RealisedPNLBeforeFees = decimal.Zero - p.status = Closed } - p.pnlHistory, err = p.upsertPNLEntry(p.pnlHistory, result) + p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) if err != nil { return err } @@ -407,6 +457,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) p.realisedPNL = p.calculateRealisedPNL(p.pnlHistory) + p.unrealisedPNL = decimal.Zero } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short @@ -427,92 +478,23 @@ func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, er if calc == nil { return nil, ErrNilPNLCalculator } - result := &PNLResult{} var price, amount decimal.Decimal - var err error - if calc.OrderBasedCalculation != nil { - price = decimal.NewFromFloat(calc.OrderBasedCalculation.Price) - amount = decimal.NewFromFloat(calc.OrderBasedCalculation.Amount) - fee := decimal.NewFromFloat(calc.OrderBasedCalculation.Fee) - // seperate this out for before here, to do this twice - if (calc.CurrentDirection.IsShort() && calc.OrderBasedCalculation.Side.IsLong() || calc.CurrentDirection.IsLong() && calc.OrderBasedCalculation.Side.IsShort()) && - calc.Exposure.LessThan(amount) { - - // latest order swaps directions! - first := amount.Sub(calc.Exposure) - second := calc.Exposure.Sub(amount).Abs() - fee = fee.Div(decimal.NewFromInt(2)) - result, err = createPNLResult( - calc.OrderBasedCalculation.Date, - calc.OrderBasedCalculation.Side, - first, - price, - fee, - calc.OpeningDirection, - calc.CurrentDirection, - calc.PNLHistory) - if err != nil { - return nil, err - } - p.pnlHistory, err = p.upsertPNLEntry(calc.PNLHistory, result) - if err != nil { - return nil, err - } - - if calc.OrderBasedCalculation.Side.IsLong() { - calc.OrderBasedCalculation.Side = Short - } else if calc.OrderBasedCalculation.Side.IsShort() { - calc.OrderBasedCalculation.Side = Long - } - if calc.OpeningDirection.IsLong() { - calc.OpeningDirection = Short - } else if calc.OpeningDirection.IsShort() { - calc.OpeningDirection = Long - } - - calc.EntryPrice = price - result, err = createPNLResult( - calc.OrderBasedCalculation.Date.Add(1), - calc.OrderBasedCalculation.Side, - second, - price, - fee, - calc.OpeningDirection, - calc.CurrentDirection, - calc.PNLHistory) - if err != nil { - return nil, err - } - return result, nil - } - result, err = createPNLResult( - calc.OrderBasedCalculation.Date, - calc.OrderBasedCalculation.Side, - amount, - price, - fee, - calc.OpeningDirection, - calc.CurrentDirection, - calc.PNLHistory) - if err != nil { - return nil, err - } - - return result, nil - } else if calc.TimeBasedCalculation != nil { - result.Time = calc.TimeBasedCalculation.Time - price = decimal.NewFromFloat(calc.TimeBasedCalculation.CurrentPrice) - diff := price.Sub(calc.EntryPrice) - result.UnrealisedPNL = calc.Exposure.Mul(diff) - result.Price = price - if len(calc.PNLHistory) > 0 { - result.RealisedPNLBeforeFees = calc.PNLHistory[len(calc.PNLHistory)-1].RealisedPNLBeforeFees - result.Exposure = calc.PNLHistory[len(calc.PNLHistory)-1].Exposure - } - return result, nil + price = decimal.NewFromFloat(calc.CurrentPrice) + amount = decimal.NewFromFloat(calc.Amount) + result, err := createPNLResult( + calc.Time, + calc.OrderDirection, + amount, + price, + calc.Fee, + calc.OpeningDirection, + calc.CurrentDirection, + calc.PNLHistory) + if err != nil { + return nil, err } - return nil, errMissingPNLCalculationFunctions + return result, nil } func createPNLResult(t time.Time, side Side, amount, price, fee decimal.Decimal, openingDirection, currentDirection Side, pnlHistory []PNLResult) (*PNLResult, error) { @@ -586,7 +568,7 @@ func (p *PositionTracker) calculateRealisedPNL(pnlHistory []PNLResult) decimal.D // upsertPNLEntry upserts an entry to PNLHistory field // with some basic checks -func (p *PositionTracker) upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, error) { +func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, error) { if entry.Time.IsZero() { return nil, errTimeUnset } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 2a448d61444..bffb18ba996 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -198,7 +198,7 @@ func TestTrackNewOrder(t *testing.T) { } err = f.TrackNewOrder(od) - if !errors.Is(err, errPositionClosed) { + if !errors.Is(err, ErrPositionClosed) { t.Error(err) } if f.currentDirection != UnknownSide { diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index e6d56b08531..9f94daa7521 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -11,12 +11,26 @@ import ( ) var ( + // ErrPositionClosed returned when attempting to amend a closed position + ErrPositionClosed = errors.New("the position is closed") + // ErrPositionsNotLoadedForExchange returned when no position data exists for an exchange + ErrPositionsNotLoadedForExchange = errors.New("no positions loaded for exchange") + // ErrPositionsNotLoadedForAsset returned when no position data exists for an asset + ErrPositionsNotLoadedForAsset = errors.New("no positions loaded for asset") + // ErrPositionsNotLoadedForPair returned when no position data exists for a pair + ErrPositionsNotLoadedForPair = errors.New("no positions loaded for pair") + // ErrNilPNLCalculator is raised when pnl calculation is requested for + // an exchange, but the fields are not set properly + ErrNilPNLCalculator = errors.New("nil pnl calculator received") + // ErrPositionLiquidated is raised when checking PNL status only for + // it to be liquidated + ErrPositionLiquidated = errors.New("position liquidated") + errExchangeNameEmpty = errors.New("exchange name empty") errNotFutureAsset = errors.New("asset type is not futures") errTimeUnset = errors.New("time unset") errMissingPNLCalculationFunctions = errors.New("futures tracker requires exchange PNL calculation functions") errOrderNotEqualToTracker = errors.New("order does not match tracker data") - errPositionClosed = errors.New("the position is closed") errPositionNotClosed = errors.New("the position is not closed") errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") errAssetMismatch = errors.New("provided asset does not match") @@ -25,16 +39,6 @@ var ( errNilOrder = errors.New("nil order received") errNoPNLHistory = errors.New("no pnl history") errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL, order is not open") - ErrPositionsNotLoadedForExchange = errors.New("no positions loaded for exchange") - ErrPositionsNotLoadedForAsset = errors.New("no positions loaded for asset") - ErrPositionsNotLoadedForPair = errors.New("no positions loaded for pair") - - // ErrNilPNLCalculator is raised when pnl calculation is requested for - // an exchange, but the fields are not set properly - ErrNilPNLCalculator = errors.New("nil pnl calculator received") - // ErrPositionLiquidated is raised when checking PNL status only for - // it to be liquidated - ErrPositionLiquidated = errors.New("position liquidated") ) // PNLCalculation is an interface to allow multiple @@ -153,33 +157,10 @@ type PNLCalculator struct{} // PNLCalculatorRequest is used to calculate PNL values // for an open position type PNLCalculatorRequest struct { - OpeningDirection Side - CurrentDirection Side - Exposure decimal.Decimal - EntryPrice decimal.Decimal - PNLHistory []PNLResult - OrderBasedCalculation *Detail - TimeBasedCalculation *TimeBasedCalculation - ExchangeBasedCalculation *ExchangeBasedCalculation -} - -// TimeBasedCalculation will update PNL values -// based on the current time -type TimeBasedCalculation struct { - Time time.Time - CurrentPrice float64 -} - -// ExchangeBasedCalculation are the fields required to -// calculate PNL using an exchange's custom PNL calculations -// eg FTX uses a different method than Binance to calculate PNL -// values -type ExchangeBasedCalculation struct { Pair currency.Pair CalculateOffline bool Underlying currency.Code Asset asset.Item - Side Side Leverage float64 EntryPrice float64 EntryAmount float64 @@ -190,6 +171,24 @@ type ExchangeBasedCalculation struct { OrderID string Fee decimal.Decimal PNLHistory []PNLResult + Exposure decimal.Decimal + OrderDirection Side + OpeningDirection Side + CurrentDirection Side +} + +// TimeBasedCalculation will update PNL values +// based on the current time +type TimeBasedCalculation struct { + Time time.Time + CurrentPrice float64 +} + +// ExchangeBasedCalculation are the fields required to +// calculate PNL using an exchange's custom PNL calculations +// eg FTX uses a different method than Binance to calculate PNL +// values +type ExchangeBasedCalculation struct { } // PNLResult stores pnl history at a point in time diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index f378ce15172..59e58674906 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -10777,6 +10777,7 @@ type GetFuturesPositionsRequest struct { EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` + Verbose bool `protobuf:"varint,8,opt,name=verbose,proto3" json:"verbose,omitempty"` } func (x *GetFuturesPositionsRequest) Reset() { @@ -10860,13 +10861,23 @@ func (x *GetFuturesPositionsRequest) GetPositionLimit() int64 { return 0 } +func (x *GetFuturesPositionsRequest) GetVerbose() bool { + if x != nil { + return x.Verbose + } + return false +} + type GetFuturesPositionsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` - Positions []*FuturePosition `protobuf:"bytes,2,rep,name=positions,proto3" json:"positions,omitempty"` + TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` + TotalRealisedPNL float64 `protobuf:"fixed64,2,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` + TotalUnrealisedPNL float64 `protobuf:"fixed64,3,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` + TotalPNL float64 `protobuf:"fixed64,4,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` + Positions []*FuturePosition `protobuf:"bytes,5,rep,name=positions,proto3" json:"positions,omitempty"` } func (x *GetFuturesPositionsResponse) Reset() { @@ -10908,6 +10919,27 @@ func (x *GetFuturesPositionsResponse) GetTotalOrders() int64 { return 0 } +func (x *GetFuturesPositionsResponse) GetTotalRealisedPNL() float64 { + if x != nil { + return x.TotalRealisedPNL + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetTotalUnrealisedPNL() float64 { + if x != nil { + return x.TotalUnrealisedPNL + } + return 0 +} + +func (x *GetFuturesPositionsResponse) GetTotalPNL() float64 { + if x != nil { + return x.TotalPNL + } + return 0 +} + func (x *GetFuturesPositionsResponse) GetPositions() []*FuturePosition { if x != nil { return x.Positions @@ -12550,7 +12582,7 @@ var file_rpc_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xf0, 0x01, 0x0a, 0x1a, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, @@ -12565,741 +12597,750 @@ var file_rpc_proto_rawDesc = []byte{ 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x75, - 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, 0x75, - 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, - 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, - 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, - 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, - 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, - 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, - 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, - 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, - 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x32, 0x9a, 0x58, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, - 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, - 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, - 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, - 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, - 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, - 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, - 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, - 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, - 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, - 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, - 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, - 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, - 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, - 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, - 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, - 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, + 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, + 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, + 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, + 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, + 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, + 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, + 0x4e, 0x4c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x24, 0x0a, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, + 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, + 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, + 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, + 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, + 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x32, 0x9a, 0x58, 0x0a, 0x0e, 0x47, 0x6f, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, + 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, + 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, + 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, + 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, + 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, + 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, + 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, - 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, - 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, - 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, - 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, - 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, - 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, - 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, - 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, - 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, - 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, - 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, - 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, - 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, - 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, + 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, - 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, - 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, - 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, - 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, + 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, + 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, + 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, + 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, - 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, - 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, - 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, - 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, - 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, + 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, - 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, + 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, + 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, + 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, + 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, + 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, + 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, - 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, - 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, - 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, - 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, - 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, - 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, - 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, - 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, - 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, - 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, - 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, - 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, + 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, - 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, - 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, - 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, - 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, - 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, - 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, + 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, + 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, - 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, - 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, - 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, - 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, - 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, - 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, - 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, + 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, + 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, - 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, - 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, - 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, - 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, - 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, - 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, - 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, - 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, - 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, - 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, - 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, - 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, - 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, - 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, - 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, + 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, + 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, + 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, + 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, + 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, + 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, + 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, + 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, + 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, + 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, - 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, - 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, - 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, - 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, - 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, - 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, - 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, + 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, + 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, + 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, + 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, + 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, + 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, - 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, - 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, - 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, - 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, + 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, - 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, - 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, - 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, - 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, - 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, - 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, + 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 88ec7207768..1a5b7bea246 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1031,12 +1031,16 @@ message GetFuturesPositionsRequest { string end_date = 5; string status = 6; int64 positionLimit = 7; + bool verbose = 8; } message GetFuturesPositionsResponse { int64 totalOrders = 1; - repeated FuturePosition positions = 2; + double totalRealisedPNL = 2; + double totalUnrealisedPNL = 3; + double totalPNL = 4; + repeated FuturePosition positions = 5; } message FuturePosition { diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 2125bd9f014..378df8be90d 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1777,6 +1777,12 @@ "required": false, "type": "string", "format": "int64" + }, + { + "name": "verbose", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ @@ -4296,6 +4302,18 @@ "type": "string", "format": "int64" }, + "totalRealisedPNL": { + "type": "number", + "format": "double" + }, + "totalUnrealisedPNL": { + "type": "number", + "format": "double" + }, + "totalPNL": { + "type": "number", + "format": "double" + }, "positions": { "type": "array", "items": { From e14840d1e6e48c94da27f70e358789bcf99d47aa Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 31 Dec 2021 15:23:18 +1100 Subject: [PATCH 049/171] beautiful tests! --- exchanges/ftx/ftx_wrapper.go | 6 - exchanges/order/futures.go | 25 +- exchanges/order/futures_test.go | 457 ++++++++++++++++++++++++-------- 3 files changed, 365 insertions(+), 123 deletions(-) diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 6ac688404e0..230b0d16778 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1273,12 +1273,6 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur return availableChains, nil } -// entry price -// side -// current price -// previous price -// amount - // CalculatePNL uses a high-tech algorithm to calculate your pnl func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { if pnl == nil { diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 9fddddd8a48..c3b187ee80e 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -64,17 +64,20 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite } c.m.Lock() defer c.m.Unlock() + if !item.IsFutures() { + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, errNotFutureAsset) + } exchM, ok := c.positionTrackerControllers[exch] if !ok { - return nil, ErrPositionsNotLoadedForExchange + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) } itemM, ok := exchM[item] if !ok { - return nil, ErrPositionsNotLoadedForAsset + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForAsset) } multiPositionTracker, ok := itemM[pair] if !ok { - return nil, ErrPositionsNotLoadedForPair + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) } return multiPositionTracker.GetPositions(), nil } @@ -204,12 +207,15 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) entryPrice: decimal.NewFromFloat(setup.EntryPrice), currentDirection: setup.Side, openingDirection: setup.Side, - useExchangePNLCalculation: e.useExchangePNLCalculations, + useExchangePNLCalculation: setup.UseExchangePNLCalculation, } - if !e.useExchangePNLCalculations { + if !setup.UseExchangePNLCalculation { // use position tracker's pnl calculation by default resp.PNLCalculation = &PNLCalculator{} } else { + if e.exchangePNLCalculation == nil { + return nil, ErrNilPNLCalculator + } resp.PNLCalculation = e.exchangePNLCalculation } return resp, nil @@ -271,7 +277,7 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { p.m.Lock() defer p.m.Unlock() - return p.calculateRealisedPNL(p.pnlHistory) + return calculateRealisedPNL(p.pnlHistory) } // GetLatestPNLSnapshot takes the latest pnl history value @@ -456,7 +462,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) - p.realisedPNL = p.calculateRealisedPNL(p.pnlHistory) + p.realisedPNL = calculateRealisedPNL(p.pnlHistory) p.unrealisedPNL = decimal.Zero } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { @@ -553,7 +559,7 @@ func createPNLResult(t time.Time, side Side, amount, price, fee decimal.Decimal, return response, nil } -func (p *PositionTracker) calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { +func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) @@ -562,8 +568,7 @@ func (p *PositionTracker) calculateRealisedPNL(pnlHistory []PNLResult) decimal.D if realisedPNL.IsZero() { return decimal.Zero } - fullyDone := realisedPNL.Sub(totalFees) - return fullyDone + return realisedPNL.Sub(totalFees) } // upsertPNLEntry upserts an entry to PNLHistory field diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index bffb18ba996..a54e60a87b8 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -23,65 +24,65 @@ func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { } func TestTrackPNL(t *testing.T) { - t.Parallel() - exch := "test" - item := asset.Futures - pair, err := currency.NewPairFromStrings("BTC", "1231") - if !errors.Is(err, nil) { - t.Error(err) - } - fPNL := &FakePNL{ - result: &PNLResult{ - Time: time.Now(), - }, - } - e := MultiPositionTracker{ - exchange: exch, - useExchangePNLCalculations: true, - exchangePNLCalculation: fPNL, - } - setup := &PositionTrackerSetup{ - Pair: pair, - Asset: item, - } - - f, err := e.SetupPositionTracker(setup) - if !errors.Is(err, nil) { - t.Error(err) - } - err = f.TrackPNLByTime(time.Now(), 0) - if !errors.Is(err, nil) { - t.Error(err) - } - fPNL.err = errMissingPNLCalculationFunctions - err = f.TrackPNLByTime(time.Now(), 0) - if !errors.Is(err, errMissingPNLCalculationFunctions) { - t.Error(err) - } + /* + t.Parallel() + exch := "test" + item := asset.Futures + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Error(err) + } + fPNL := &FakePNL{ + result: &PNLResult{ + Time: time.Now(), + }, + } + e := MultiPositionTracker{ + exchange: exch, + useExchangePNLCalculations: true, + exchangePNLCalculation: fPNL, + } + setup := &PositionTrackerSetup{ + Pair: pair, + Asset: item, + UseExchangePNLCalculation: true, + } + + f, err := e.SetupPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + + */ } func TestUpsertPNLEntry(t *testing.T) { t.Parallel() - f := &PositionTracker{} - err := f.upsertPNLEntry(PNLResult{}) + var results []PNLResult + result := &PNLResult{} + _, err := upsertPNLEntry(results, result) if !errors.Is(err, errTimeUnset) { t.Error(err) } tt := time.Now() - err = f.upsertPNLEntry(PNLResult{Time: tt}) + result.Time = tt + list, err := upsertPNLEntry(results, result) if !errors.Is(err, nil) { t.Error(err) } - if len(f.pnlHistory) != 1 { - t.Errorf("expected 1 received %v", len(f.pnlHistory)) + if len(list) != 1 { + t.Errorf("expected 1 received %v", len(list)) } - - err = f.upsertPNLEntry(PNLResult{Time: tt}) + result.Fee = decimal.NewFromInt(1337) + list, err = upsertPNLEntry(results, result) if !errors.Is(err, nil) { t.Error(err) } - if len(f.pnlHistory) != 1 { - t.Errorf("expected 1 received %v", len(f.pnlHistory)) + if len(list) != 1 { + t.Errorf("expected 1 received %v", len(list)) + } + if !list[0].Fee.Equal(result.Fee) { + t.Errorf("expected %v received %v", result.Fee, list[0].Fee) } } @@ -209,7 +210,7 @@ func TestTrackNewOrder(t *testing.T) { } } -func TestSetupFuturesTracker(t *testing.T) { +func TestSetupMultiPositionTracker(t *testing.T) { t.Parallel() _, err := SetupMultiPositionTracker(nil) @@ -404,24 +405,6 @@ func TestPositionControllerTestTrackNewOrder(t *testing.T) { } } -func TestGetRealisedPNL(t *testing.T) { - t.Parallel() - pt := PositionTracker{} - _, err := pt.GetRealisedPNL() - if !errors.Is(err, errPositionNotClosed) { - t.Error(err) - } - pt.status = Closed - pt.realisedPNL = decimal.NewFromInt(1337) - result, err := pt.GetRealisedPNL() - if !errors.Is(err, nil) { - t.Error(err) - } - if !pt.realisedPNL.Equal(result) { - t.Errorf("expected 1337, received '%v'", result) - } -} - func TestGetLatestPNLSnapshot(t *testing.T) { t.Parallel() pt := PositionTracker{} @@ -446,66 +429,326 @@ func TestGetLatestPNLSnapshot(t *testing.T) { } } -func TestCalculatePNL(t *testing.T) { +func TestGetRealisedPNL(t *testing.T) { t.Parallel() - pt := PositionTracker{} - _, err := pt.CalculatePNL(nil) - if !errors.Is(err, ErrNilPNLCalculator) { - t.Error(err) + p := PositionTracker{} + result := p.GetRealisedPNL() + if !result.IsZero() { + t.Error("expected zero") } +} - _, err = pt.CalculatePNL(&PNLCalculatorRequest{}) - if !errors.Is(err, errMissingPNLCalculationFunctions) { - t.Error(err) +func TestGetStats(t *testing.T) { + t.Parallel() + + p := &PositionTracker{} + stats := p.GetStats() + if len(stats.Orders) != 0 { + t.Error("expected 0") } - tt := time.Now() - result, err := pt.CalculatePNL(&PNLCalculatorRequest{ - TimeBasedCalculation: &TimeBasedCalculation{ - Time: tt, - CurrentPrice: 1337, - }, + + p.exchange = "test" + stats = p.GetStats() + if stats.Exchange != p.exchange { + t.Errorf("expected '%v' received '%v'", p.exchange, stats.Exchange) + } + + p = nil + stats = p.GetStats() + if len(stats.Orders) != 0 { + t.Error("expected 0") + } +} + +func TestGetPositions(t *testing.T) { + t.Parallel() + p := &MultiPositionTracker{} + positions := p.GetPositions() + if len(positions) > 0 { + t.Error("expected 0") + } + + p.positions = append(p.positions, &PositionTracker{ + exchange: "test", }) + positions = p.GetPositions() + if len(positions) != 1 { + t.Fatal("expected 1") + } + if positions[0].exchange != "test" { + t.Error("expected 'test'") + } + + p = nil + positions = p.GetPositions() + if len(positions) > 0 { + t.Error("expected 0") + } + +} + +func TestGetPositionsForExchange(t *testing.T) { + t.Parallel() + c := &PositionController{} + p := currency.NewPair(currency.BTC, currency.USDT) + pos, err := c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + if len(pos) != 0 { + t.Error("expected zero") + } + c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers["test"] = nil + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForAsset) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + c.positionTrackerControllers["test"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers["test"][asset.Futures] = nil + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForPair) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) + } + pos, err = c.GetPositionsForExchange("test", asset.Spot, p) + if !errors.Is(err, errNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, errNotFutureAsset) + } + + c.positionTrackerControllers["test"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ + exchange: "test", + } + + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) if !errors.Is(err, nil) { - t.Error(err) + t.Errorf("received '%v' expected '%v", err, nil) } - if !result.Time.Equal(tt) { - t.Error("unexpected result") + if len(pos) != 0 { + t.Fatal("expected zero") + } + c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ + exchange: "test", + positions: []*PositionTracker{ + { + exchange: "test", + }, + }, + } + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) } + if len(pos) != 1 { + t.Fatal("expected 1") + } + if pos[0].exchange != "test" { + t.Error("expected test") + } + c = nil + pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } +} - pt.status = Open - pt.currentDirection = Long - result, err = pt.CalculatePNL(&PNLCalculatorRequest{ - OrderBasedCalculation: &Detail{ - Date: tt, - Price: 1337, - Exchange: "test", - AssetType: asset.Spot, - Side: Long, - Status: Active, - Pair: currency.NewPair(currency.BTC, currency.USDT), - Amount: 5, +func TestCalculateRealisedPNL(t *testing.T) { + t.Parallel() + result := calculateRealisedPNL(nil) + if !result.IsZero() { + t.Error("expected zero") + } + result = calculateRealisedPNL([]PNLResult{ + { + RealisedPNLBeforeFees: decimal.NewFromInt(1337), }, }) - if !errors.Is(err, nil) { - t.Error(err) + if !result.Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") } - pt.exposure = decimal.NewFromInt(5) - result, err = pt.CalculatePNL(&PNLCalculatorRequest{ - OrderBasedCalculation: &Detail{ - Date: tt, - Price: 1337, - Exchange: "test", - AssetType: asset.Spot, - Side: Short, - Status: Active, - Pair: currency.NewPair(currency.BTC, currency.USDT), - Amount: 10, + result = calculateRealisedPNL([]PNLResult{ + { + RealisedPNLBeforeFees: decimal.NewFromInt(1339), + Fee: decimal.NewFromInt(2), + }, + { + RealisedPNLBeforeFees: decimal.NewFromInt(2), + Fee: decimal.NewFromInt(2), }, }) - if !errors.Is(err, nil) { + if !result.Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") + } +} + +func TestCreatePNLResult(t *testing.T) { + t.Parallel() + result, err := createPNLResult(time.Now(), Buy, decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.NewFromInt(1), Long, Short, nil) + if err != nil { t.Error(err) } + t.Log(result) +} + +func TestTrackPNLByTime(t *testing.T) { + t.Parallel() + p := &PositionTracker{} + err := p.TrackPNLByTime(time.Now(), 1) + if err != nil { + t.Error(err) + } + + err = p.TrackPNLByTime(time.Now(), 2) + if err != nil { + t.Error(err) + } + if !p.latestPrice.Equal(decimal.NewFromInt(2)) { + t.Error("expected 2") + } + t.Log(p.latestPrice) + +} + +func TestSetupPositionTracker(t *testing.T) { + t.Parallel() + m := &MultiPositionTracker{} + p, err := m.SetupPositionTracker(nil) + if !errors.Is(err, errExchangeNameEmpty) { + t.Errorf("received '%v' expected '%v", err, errExchangeNameEmpty) + } + if p != nil { + t.Error("expected nil") + } + m.exchange = "test" + p, err = m.SetupPositionTracker(nil) + if !errors.Is(err, errNilSetup) { + t.Errorf("received '%v' expected '%v", err, errNilSetup) + } + if p != nil { + t.Error("expected nil") + } + + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Spot, + }) + if !errors.Is(err, errNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, errNotFutureAsset) + } + if p != nil { + t.Error("expected nil") + } + + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + }) + if !errors.Is(err, ErrPairIsEmpty) { + t.Errorf("received '%v' expected '%v", err, ErrPairIsEmpty) + } + if p != nil { + t.Error("expected nil") + } + + cp := currency.NewPair(currency.BTC, currency.USDT) + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + Pair: cp, + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if p == nil { + t.Error("expected nil") + } + if p.exchange != "test" { + t.Error("expected test") + } + + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + Pair: cp, + UseExchangePNLCalculation: true, + }) + if !errors.Is(err, ErrNilPNLCalculator) { + t.Errorf("received '%v' expected '%v", err, ErrNilPNLCalculator) + } + m.exchangePNLCalculation = &PNLCalculator{} + p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + Asset: asset.Futures, + Pair: cp, + UseExchangePNLCalculation: true, + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if !p.useExchangePNLCalculation { + t.Error("expected true") + } +} + +func TestCalculatePNL(t *testing.T) { + t.Parallel() + /* + t.Parallel() + pt := PositionTracker{} + _, err := pt.CalculatePNL(nil) + if !errors.Is(err, ErrNilPNLCalculator) { + t.Error(err) + } + + _, err = pt.CalculatePNL(&PNLCalculatorRequest{}) + if !errors.Is(err, errMissingPNLCalculationFunctions) { + t.Error(err) + } + tt := time.Now() + result, err := pt.CalculatePNL(&PNLCalculatorRequest{ + TimeBasedCalculation: &TimeBasedCalculation{ + Time: tt, + CurrentPrice: 1337, + }, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + if !result.Time.Equal(tt) { + t.Error("unexpected result") + } + + pt.status = Open + pt.currentDirection = Long + result, err = pt.CalculatePNL(&PNLCalculatorRequest{ + OrderBasedCalculation: &Detail{ + Date: tt, + Price: 1337, + Exchange: "test", + AssetType: asset.Spot, + Side: Long, + Status: Active, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Amount: 5, + }, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + + pt.exposure = decimal.NewFromInt(5) + result, err = pt.CalculatePNL(&PNLCalculatorRequest{ + OrderBasedCalculation: &Detail{ + Date: tt, + Price: 1337, + Exchange: "test", + AssetType: asset.Spot, + Side: Short, + Status: Active, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Amount: 10, + }, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + + */ - // todo do a proper test of values to ensure correct split calculation! } From ca85e3a8d16ea378b4ce20a1561129a7aa2b7c3d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 4 Jan 2022 17:05:45 +1100 Subject: [PATCH 050/171] rejiggles tests to polish --- exchanges/ftx/ftx_test.go | 77 +++++++------ exchanges/ftx/ftx_wrapper.go | 46 ++++---- exchanges/order/futures.go | 111 +++++++------------ exchanges/order/futures_test.go | 181 ++++++++----------------------- exchanges/order/futures_types.go | 17 ++- 5 files changed, 165 insertions(+), 267 deletions(-) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index b3f29bbe182..48385c26778 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -30,7 +30,7 @@ const ( apiSecret = "" subaccount = "" canManipulateRealOrders = false - spotPair = "FTT/BTC" + spotPairStr = "FTT/BTC" futuresPair = "DOGE-PERP" testLeverageToken = "ADAMOON" @@ -42,7 +42,10 @@ const ( authEndTime = validFTTBTCEndTime ) -var f FTX +var ( + f FTX + spotPair = currency.NewPair(currency.FTT, currency.BTC) +) func TestMain(m *testing.M) { f.SetDefaults() @@ -119,7 +122,7 @@ func TestGetHistoricalIndex(t *testing.T) { func TestGetMarket(t *testing.T) { t.Parallel() - _, err := f.GetMarket(context.Background(), spotPair) + _, err := f.GetMarket(context.Background(), spotPairStr) if err != nil { t.Error(err) } @@ -127,7 +130,7 @@ func TestGetMarket(t *testing.T) { func TestGetOrderbook(t *testing.T) { t.Parallel() - _, err := f.GetOrderbook(context.Background(), spotPair, 5) + _, err := f.GetOrderbook(context.Background(), spotPairStr, 5) if err != nil { t.Error(err) } @@ -141,13 +144,13 @@ func TestGetTrades(t *testing.T) { t.Error("empty market should return an error") } _, err = f.GetTrades(context.Background(), - spotPair, validFTTBTCEndTime, validFTTBTCStartTime, 5) + spotPairStr, validFTTBTCEndTime, validFTTBTCStartTime, 5) if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } // test optional params var trades []TradeData - trades, err = f.GetTrades(context.Background(), spotPair, 0, 0, 0) + trades, err = f.GetTrades(context.Background(), spotPairStr, 0, 0, 0) if err != nil { t.Error(err) } @@ -155,7 +158,7 @@ func TestGetTrades(t *testing.T) { t.Error("default limit should return 20 items") } trades, err = f.GetTrades(context.Background(), - spotPair, validFTTBTCStartTime, validFTTBTCEndTime, 5) + spotPairStr, validFTTBTCStartTime, validFTTBTCEndTime, 5) if err != nil { t.Error(err) } @@ -163,7 +166,7 @@ func TestGetTrades(t *testing.T) { t.Error("limit of 5 should return 5 items") } trades, err = f.GetTrades(context.Background(), - spotPair, invalidFTTBTCStartTime, invalidFTTBTCEndTime, 5) + spotPairStr, invalidFTTBTCStartTime, invalidFTTBTCEndTime, 5) if err != nil { t.Error(err) } @@ -182,19 +185,19 @@ func TestGetHistoricalData(t *testing.T) { } // test empty resolution _, err = f.GetHistoricalData(context.Background(), - spotPair, 0, 5, time.Time{}, time.Time{}) + spotPairStr, 0, 5, time.Time{}, time.Time{}) if err == nil { t.Error("empty resolution should return an error") } _, err = f.GetHistoricalData(context.Background(), - spotPair, 86400, 5, time.Unix(validFTTBTCEndTime, 0), + spotPairStr, 86400, 5, time.Unix(validFTTBTCEndTime, 0), time.Unix(validFTTBTCStartTime, 0)) if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } var o []OHLCVData o, err = f.GetHistoricalData(context.Background(), - spotPair, 86400, 5, time.Time{}, time.Time{}) + spotPairStr, 86400, 5, time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -202,7 +205,7 @@ func TestGetHistoricalData(t *testing.T) { t.Error("limit of 5 should return 5 items") } o, err = f.GetHistoricalData(context.Background(), - spotPair, 86400, 5, time.Unix(invalidFTTBTCStartTime, 0), + spotPairStr, 86400, 5, time.Unix(invalidFTTBTCStartTime, 0), time.Unix(invalidFTTBTCEndTime, 0)) if err != nil { t.Error(err) @@ -525,7 +528,7 @@ func TestGetOpenOrders(t *testing.T) { if err != nil { t.Error(err) } - _, err = f.GetOpenOrders(context.Background(), spotPair) + _, err = f.GetOpenOrders(context.Background(), spotPairStr) if err != nil { t.Error(err) } @@ -542,12 +545,12 @@ func TestFetchOrderHistory(t *testing.T) { t.Error(err) } _, err = f.FetchOrderHistory(context.Background(), - spotPair, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), "2") + spotPairStr, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), "2") if err != nil { t.Error(err) } _, err = f.FetchOrderHistory(context.Background(), - spotPair, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), "2") + spotPairStr, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), "2") if err != errStartTimeCannotBeAfterEndTime { t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } @@ -563,7 +566,7 @@ func TestGetOpenTriggerOrders(t *testing.T) { if err != nil { t.Error(err) } - _, err = f.GetOpenTriggerOrders(context.Background(), spotPair, "") + _, err = f.GetOpenTriggerOrders(context.Background(), spotPairStr, "") if err != nil { t.Error(err) } @@ -591,12 +594,12 @@ func TestGetTriggerOrderHistory(t *testing.T) { t.Error(err) } _, err = f.GetTriggerOrderHistory(context.Background(), - spotPair, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1") + spotPairStr, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1") if err != nil { t.Error(err) } _, err = f.GetTriggerOrderHistory(context.Background(), - spotPair, + spotPairStr, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), order.Buy.Lower(), @@ -606,7 +609,7 @@ func TestGetTriggerOrderHistory(t *testing.T) { t.Error(err) } _, err = f.GetTriggerOrderHistory(context.Background(), - spotPair, + spotPairStr, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), order.Buy.Lower(), @@ -623,7 +626,7 @@ func TestOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } _, err := f.Order(context.Background(), - spotPair, + spotPairStr, order.Buy.Lower(), "limit", false, false, false, @@ -640,7 +643,7 @@ func TestSubmitOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set correctly") } - currencyPair, err := currency.NewPairFromString(spotPair) + currencyPair, err := currency.NewPairFromString(spotPairStr) if err != nil { t.Fatal(err) } @@ -666,7 +669,7 @@ func TestTriggerOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } _, err := f.TriggerOrder(context.Background(), - spotPair, + spotPairStr, order.Buy.Lower(), order.Stop.Lower(), "", "", @@ -682,7 +685,7 @@ func TestCancelOrder(t *testing.T) { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set correctly") } - currencyPair, err := currency.NewPairFromString(spotPair) + currencyPair, err := currency.NewPairFromString(spotPairStr) if err != nil { t.Fatal(err) } @@ -741,7 +744,8 @@ func TestGetFills(t *testing.T) { t.Skip() } // optional params - _, err := f.GetFills(context.Background(), "", "", time.Time{}, time.Time{}) + + _, err := f.GetFills(context.Background(), spotPair, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -1267,7 +1271,7 @@ func TestGetOTCQuoteStatus(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("API keys required but not set, skipping test") } - _, err := f.GetOTCQuoteStatus(context.Background(), spotPair, "1") + _, err := f.GetOTCQuoteStatus(context.Background(), spotPairStr, "1") if err != nil { t.Error(err) } @@ -1811,10 +1815,10 @@ func TestCalculatePNLFromOrders(t *testing.T) { if pnl := pos[1].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0148)) { t.Errorf("expected nil err, received '%v', expected 0.0148, received '%v'", err, pnl) } - if pnl := pos[2].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0092)) { + if pnl := pos[2].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0004)) { t.Errorf("expected nil err, received '%v', expected 0.0092, received '%v'", err, pnl) } - if pnl := pos[3].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0054)) { + if pnl := pos[3].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0098)) { t.Errorf("expected nil err, received '%v', expected -0.0054, received '%v'", err, pnl) } if pnl := pos[4].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0387)) { @@ -1826,7 +1830,9 @@ func TestCalculatePNLFromOrders(t *testing.T) { } func TestScaleCollateral(t *testing.T) { - f.Verbose = true + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } ai, err := f.GetAccountInfo(context.Background()) if err != nil { t.Error(err) @@ -1886,8 +1892,10 @@ func TestScaleCollateral(t *testing.T) { } func TestCalculatePNLFromOrders1(t *testing.T) { - f.Verbose = true - resp, err := f.GetFills(context.Background(), "BTC-1231", "200", time.Time{}, time.Time{}) + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + resp, err := f.GetFills(context.Background(), spotPair, "200", time.Time{}, time.Time{}) result := resp sort.Slice(result, func(i, j int) bool { return result[i].Time.Before(result[j].Time) @@ -1937,8 +1945,10 @@ func TestCalculatePNLFromOrders1(t *testing.T) { } positions := p.GetPositions() for i := range positions { - sn, _ := positions[i].GetLatestPNLSnapshot() - t.Logf("%v %v", sn.Time, positions[i].GetRealisedPNL()) + _, err = positions[i].GetLatestPNLSnapshot() + if err != nil { + t.Error(err) + } } } @@ -1991,7 +2001,8 @@ func TestCalculatePNLFromOrders3(t *testing.T) { Pair: pair, Underlying: pair.Base, ExchangePNLCalculation: &f, - UseExchangePNLCalculation: false, + UseExchangePNLCalculation: true, + OfflineCalculation: true, } p, err := order.SetupMultiPositionTracker(setup) if err != nil { diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 230b0d16778..9dbb8793fbe 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1280,35 +1280,37 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e } var result order.PNLResult result.Time = pnl.Time + if pnl.CalculateOffline { + result.Fee = pnl.Fee + result.UnrealisedPNL = pnl.Amount.Mul(pnl.CurrentPrice.Sub(pnl.PreviousPrice)) + result.Price = pnl.CurrentPrice + ftxPNLCalculation := pnl.Amount.Mul(pnl.CurrentPrice.Sub(pnl.EntryPrice)) + result.RealisedPNLBeforeFees = ftxPNLCalculation.Sub(result.UnrealisedPNL) + return &result, nil + } - if !pnl.CalculateOffline { - info, err := f.GetAccountInfo(context.Background()) + ep := pnl.EntryPrice.InexactFloat64() + info, err := f.GetAccountInfo(context.Background()) + if err != nil { + return nil, err + } + if info.Liquidating || info.Collateral == 0 { + return &result, order.ErrPositionLiquidated + } + for i := range info.Positions { + pair, err := currency.NewPairFromString(info.Positions[i].Future) if err != nil { return nil, err } - if info.Liquidating || info.Collateral == 0 { - return &result, order.ErrPositionLiquidated + if !pnl.Pair.Equal(pair) { + continue } - for i := range info.Positions { - pair, err := currency.NewPairFromString(info.Positions[i].Future) - if err != nil { - return nil, err - } - if !pnl.Pair.Equal(pair) { - continue - } - if info.Positions[i].EntryPrice == pnl.EntryPrice { - result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) - result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) - result.Price = decimal.NewFromFloat(info.Positions[i].Cost) - return &result, nil - } + if info.Positions[i].EntryPrice == ep { + result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) + result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) + result.Price = decimal.NewFromFloat(info.Positions[i].Cost) } } - uPNL := pnl.Amount * (pnl.CurrentPrice - pnl.PreviousPrice) - result.RealisedPNLBeforeFees = result.RealisedPNLBeforeFees.Add(result.UnrealisedPNL) - result.UnrealisedPNL = decimal.NewFromFloat(uPNL) - result.Price = decimal.NewFromFloat(pnl.CurrentPrice) return &result, nil } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index c3b187ee80e..62e0816ff76 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -160,7 +160,7 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { } setup := &PositionTrackerSetup{ Pair: d.Pair, - EntryPrice: d.Price, + EntryPrice: decimal.NewFromFloat(d.Price), Underlying: d.Pair.Base, Asset: d.AssetType, Side: d.Side, @@ -204,10 +204,11 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) contractPair: setup.Pair, underlyingAsset: setup.Underlying, status: Open, - entryPrice: decimal.NewFromFloat(setup.EntryPrice), + entryPrice: setup.EntryPrice, currentDirection: setup.Side, openingDirection: setup.Side, useExchangePNLCalculation: setup.UseExchangePNLCalculation, + offlinePNLCalculation: e.offlinePNLCalculation, } if !setup.UseExchangePNLCalculation { // use position tracker's pnl calculation by default @@ -275,6 +276,9 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro // GetRealisedPNL returns the realised pnl if the order // is closed func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { + if p == nil { + return decimal.Zero + } p.m.Lock() defer p.m.Unlock() return calculateRealisedPNL(p.pnlHistory) @@ -365,14 +369,18 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { var result *PNLResult var err error + var price, amount, leverage decimal.Decimal + price = decimal.NewFromFloat(d.Price) + amount = decimal.NewFromFloat(d.Amount) + leverage = decimal.NewFromFloat(d.Leverage) cal := &PNLCalculatorRequest{ Underlying: p.underlyingAsset, Asset: p.asset, OrderDirection: d.Side, - Leverage: d.Leverage, - EntryPrice: p.entryPrice.InexactFloat64(), - Amount: d.Amount, - CurrentPrice: d.Price, + Leverage: leverage, + EntryPrice: p.entryPrice, + Amount: amount, + CurrentPrice: price, Pair: p.contractPair, Time: d.Date, OpeningDirection: p.openingDirection, @@ -380,11 +388,11 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { PNLHistory: p.pnlHistory, Exposure: p.exposure, Fee: decimal.NewFromFloat(d.Fee), + CalculateOffline: p.offlinePNLCalculation, } if len(p.pnlHistory) != 0 { - cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price.InexactFloat64() + cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price } - amount := decimal.NewFromFloat(d.Amount) if (cal.OrderDirection.IsShort() && cal.CurrentDirection.IsLong() || cal.OrderDirection.IsLong() && cal.CurrentDirection.IsShort()) && cal.Exposure.LessThan(amount) { // latest order swaps directions! @@ -392,15 +400,8 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { first := amount.Sub(cal.Exposure) second := cal.Exposure.Sub(amount).Abs() cal.Fee = cal.Fee.Div(decimal.NewFromInt(2)) - result, err = createPNLResult( - cal.Time, - cal.OrderDirection, - first, - decimal.NewFromFloat(d.Price), - cal.Fee, - cal.OpeningDirection, - cal.CurrentDirection, - cal.PNLHistory) + cal.Amount = first + result, err = p.PNLCalculation.CalculatePNL(cal) if err != nil { return err } @@ -419,17 +420,10 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } else if cal.OpeningDirection.IsShort() { cal.OpeningDirection = Long } - - cal.EntryPrice = d.Price - result, err = createPNLResult( - cal.Time.Add(1), - cal.OrderDirection, - second, - decimal.NewFromFloat(d.Price), - cal.Fee, - cal.OpeningDirection, - cal.CurrentDirection, - cal.PNLHistory) + cal.Amount = second + cal.EntryPrice = price + cal.Time = cal.Time.Add(1) + result, err = p.PNLCalculation.CalculatePNL(cal) } else { result, err = p.PNLCalculation.CalculatePNL(cal) if err != nil { @@ -484,77 +478,56 @@ func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, er if calc == nil { return nil, ErrNilPNLCalculator } - var price, amount decimal.Decimal - price = decimal.NewFromFloat(calc.CurrentPrice) - amount = decimal.NewFromFloat(calc.Amount) - result, err := createPNLResult( - calc.Time, - calc.OrderDirection, - amount, - price, - calc.Fee, - calc.OpeningDirection, - calc.CurrentDirection, - calc.PNLHistory) - if err != nil { - return nil, err - } - - return result, nil -} - -func createPNLResult(t time.Time, side Side, amount, price, fee decimal.Decimal, openingDirection, currentDirection Side, pnlHistory []PNLResult) (*PNLResult, error) { var previousPNL *PNLResult - if len(pnlHistory) > 0 { - previousPNL = &pnlHistory[len(pnlHistory)-1] + if len(calc.PNLHistory) > 0 { + previousPNL = &calc.PNLHistory[len(calc.PNLHistory)-1] } var prevExposure decimal.Decimal if previousPNL != nil { prevExposure = previousPNL.Exposure } var currentExposure, realisedPNL, unrealisedPNL, first, second decimal.Decimal - if openingDirection.IsLong() { - first = price + if calc.OpeningDirection.IsLong() { + first = calc.CurrentPrice if previousPNL != nil { second = previousPNL.Price } - } else if openingDirection.IsShort() { + } else if calc.OpeningDirection.IsShort() { if previousPNL != nil { first = previousPNL.Price - second = price + second = calc.CurrentPrice } } switch { - case currentDirection.IsShort() && side.IsShort(), - currentDirection.IsLong() && side.IsLong(): + case calc.OpeningDirection.IsShort() && calc.OrderDirection.IsShort(), + calc.OpeningDirection.IsLong() && calc.OrderDirection.IsLong(): // appending to your position - currentExposure = prevExposure.Add(amount) + currentExposure = prevExposure.Add(calc.Amount) unrealisedPNL = currentExposure.Mul(first.Sub(second)) - case currentDirection.IsShort() && side.IsLong(), - currentDirection.IsLong() && side.IsShort(): + case calc.OpeningDirection.IsShort() && calc.OrderDirection.IsLong(), + calc.OpeningDirection.IsLong() && calc.OrderDirection.IsShort(): // selling/closing your position by "amount" - currentExposure = prevExposure.Sub(amount) + currentExposure = prevExposure.Sub(calc.Amount) unrealisedPNL = currentExposure.Mul(first.Sub(second)) - step1 := first.Sub(second) - realisedPNL = amount.Mul(step1) + realisedPNL = calc.Amount.Mul(first.Sub(second)) default: - return nil, fmt.Errorf("%v %v %v %w", currentDirection, side, currentExposure, errCannotCalculateUnrealisedPNL) + return nil, fmt.Errorf("%w openinig direction: '%v' order direction: '%v' exposure: '%v'", errCannotCalculateUnrealisedPNL, calc.OpeningDirection, calc.OrderDirection, currentExposure) } - totalFees := fee - for i := range pnlHistory { - totalFees = totalFees.Add(pnlHistory[i].Fee) + totalFees := calc.Fee + for i := range calc.PNLHistory { + totalFees = totalFees.Add(calc.PNLHistory[i].Fee) } if !unrealisedPNL.IsZero() { unrealisedPNL = unrealisedPNL.Sub(totalFees) } response := &PNLResult{ - Time: t, + Time: calc.Time, UnrealisedPNL: unrealisedPNL, RealisedPNLBeforeFees: realisedPNL, - Price: price, + Price: calc.CurrentPrice, Exposure: currentExposure, - Fee: fee, + Fee: calc.Fee, } return response, nil } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index a54e60a87b8..6e3b02c8728 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -23,39 +23,6 @@ func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { return f.result, nil } -func TestTrackPNL(t *testing.T) { - /* - t.Parallel() - exch := "test" - item := asset.Futures - pair, err := currency.NewPairFromStrings("BTC", "1231") - if !errors.Is(err, nil) { - t.Error(err) - } - fPNL := &FakePNL{ - result: &PNLResult{ - Time: time.Now(), - }, - } - e := MultiPositionTracker{ - exchange: exch, - useExchangePNLCalculations: true, - exchangePNLCalculation: fPNL, - } - setup := &PositionTrackerSetup{ - Pair: pair, - Asset: item, - UseExchangePNLCalculation: true, - } - - f, err := e.SetupPositionTracker(setup) - if !errors.Is(err, nil) { - t.Error(err) - } - - */ -} - func TestUpsertPNLEntry(t *testing.T) { t.Parallel() var results []PNLResult @@ -66,23 +33,23 @@ func TestUpsertPNLEntry(t *testing.T) { } tt := time.Now() result.Time = tt - list, err := upsertPNLEntry(results, result) + results, err = upsertPNLEntry(results, result) if !errors.Is(err, nil) { t.Error(err) } - if len(list) != 1 { - t.Errorf("expected 1 received %v", len(list)) + if len(results) != 1 { + t.Errorf("expected 1 received %v", len(results)) } result.Fee = decimal.NewFromInt(1337) - list, err = upsertPNLEntry(results, result) + results, err = upsertPNLEntry(results, result) if !errors.Is(err, nil) { t.Error(err) } - if len(list) != 1 { - t.Errorf("expected 1 received %v", len(list)) + if len(results) != 1 { + t.Errorf("expected 1 received %v", len(results)) } - if !list[0].Fee.Equal(result.Fee) { - t.Errorf("expected %v received %v", result.Fee, list[0].Fee) + if !results[0].Fee.Equal(result.Fee) { + t.Errorf("expected %v received %v", result.Fee, results[0].Fee) } } @@ -135,7 +102,7 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, errTimeUnset) { t.Error(err) } - + f.openingDirection = Long od.Date = time.Now() err = f.TrackNewOrder(od) if !errors.Is(err, nil) { @@ -342,9 +309,6 @@ func TestExchangeTrackNewOrder(t *testing.T) { ID: "2", Amount: 2, }) - if !errors.Is(err, errCannotCalculateUnrealisedPNL) { - t.Error(err) - } if len(resp.positions) != 2 { t.Errorf("expected '2' received %v", len(resp.positions)) } @@ -582,34 +546,6 @@ func TestCalculateRealisedPNL(t *testing.T) { } } -func TestCreatePNLResult(t *testing.T) { - t.Parallel() - result, err := createPNLResult(time.Now(), Buy, decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.NewFromInt(1), Long, Short, nil) - if err != nil { - t.Error(err) - } - t.Log(result) -} - -func TestTrackPNLByTime(t *testing.T) { - t.Parallel() - p := &PositionTracker{} - err := p.TrackPNLByTime(time.Now(), 1) - if err != nil { - t.Error(err) - } - - err = p.TrackPNLByTime(time.Now(), 2) - if err != nil { - t.Error(err) - } - if !p.latestPrice.Equal(decimal.NewFromInt(2)) { - t.Error("expected 2") - } - t.Log(p.latestPrice) - -} - func TestSetupPositionTracker(t *testing.T) { t.Parallel() m := &MultiPositionTracker{} @@ -688,67 +624,44 @@ func TestSetupPositionTracker(t *testing.T) { func TestCalculatePNL(t *testing.T) { t.Parallel() - /* - t.Parallel() - pt := PositionTracker{} - _, err := pt.CalculatePNL(nil) - if !errors.Is(err, ErrNilPNLCalculator) { - t.Error(err) - } - - _, err = pt.CalculatePNL(&PNLCalculatorRequest{}) - if !errors.Is(err, errMissingPNLCalculationFunctions) { - t.Error(err) - } - tt := time.Now() - result, err := pt.CalculatePNL(&PNLCalculatorRequest{ - TimeBasedCalculation: &TimeBasedCalculation{ - Time: tt, - CurrentPrice: 1337, - }, - }) - if !errors.Is(err, nil) { - t.Error(err) - } - if !result.Time.Equal(tt) { - t.Error("unexpected result") - } - - pt.status = Open - pt.currentDirection = Long - result, err = pt.CalculatePNL(&PNLCalculatorRequest{ - OrderBasedCalculation: &Detail{ - Date: tt, - Price: 1337, - Exchange: "test", - AssetType: asset.Spot, - Side: Long, - Status: Active, - Pair: currency.NewPair(currency.BTC, currency.USDT), - Amount: 5, - }, - }) - if !errors.Is(err, nil) { - t.Error(err) - } - - pt.exposure = decimal.NewFromInt(5) - result, err = pt.CalculatePNL(&PNLCalculatorRequest{ - OrderBasedCalculation: &Detail{ - Date: tt, - Price: 1337, - Exchange: "test", - AssetType: asset.Spot, - Side: Short, - Status: Active, - Pair: currency.NewPair(currency.BTC, currency.USDT), - Amount: 10, - }, - }) - if !errors.Is(err, nil) { - t.Error(err) - } + p := &PNLCalculator{} + _, err := p.CalculatePNL(nil) + if !errors.Is(err, ErrNilPNLCalculator) { + t.Errorf("received '%v' expected '%v", err, ErrNilPNLCalculator) + } + _, err = p.CalculatePNL(&PNLCalculatorRequest{}) + if !errors.Is(err, errCannotCalculateUnrealisedPNL) { + t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) + } - */ + _, err = p.CalculatePNL(&PNLCalculatorRequest{ + OrderDirection: Short, + CurrentDirection: Long, + }) + if !errors.Is(err, errCannotCalculateUnrealisedPNL) { + t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) + } + +} + +func TestTrackPNLByTime(t *testing.T) { + t.Parallel() + p := &PositionTracker{} + err := p.TrackPNLByTime(time.Now(), 1) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + err = p.TrackPNLByTime(time.Now(), 2) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if !p.latestPrice.Equal(decimal.NewFromInt(2)) { + t.Error("expected 2") + } + p = nil + err = p.TrackPNLByTime(time.Now(), 2) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 9f94daa7521..df7f263546c 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -31,14 +31,13 @@ var ( errTimeUnset = errors.New("time unset") errMissingPNLCalculationFunctions = errors.New("futures tracker requires exchange PNL calculation functions") errOrderNotEqualToTracker = errors.New("order does not match tracker data") - errPositionNotClosed = errors.New("the position is not closed") errPositionDiscrepancy = errors.New("there is a position considered open, but it is not the latest, please review") errAssetMismatch = errors.New("provided asset does not match") errEmptyUnderlying = errors.New("underlying asset unset") errNilSetup = errors.New("nil setup received") errNilOrder = errors.New("nil order received") errNoPNLHistory = errors.New("no pnl history") - errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL, order is not open") + errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL") ) // PNLCalculation is an interface to allow multiple @@ -132,7 +131,7 @@ type PositionTracker struct { // setup a position tracker type PositionTrackerSetup struct { Pair currency.Pair - EntryPrice float64 + EntryPrice decimal.Decimal Underlying currency.Code Asset asset.Item Side Side @@ -161,12 +160,12 @@ type PNLCalculatorRequest struct { CalculateOffline bool Underlying currency.Code Asset asset.Item - Leverage float64 - EntryPrice float64 - EntryAmount float64 - Amount float64 - CurrentPrice float64 - PreviousPrice float64 + Leverage decimal.Decimal + EntryPrice decimal.Decimal + EntryAmount decimal.Decimal + Amount decimal.Decimal + CurrentPrice decimal.Decimal + PreviousPrice decimal.Decimal Time time.Time OrderID string Fee decimal.Decimal From 61a345df260c84f8dd8197d7465280482ed41969 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 5 Jan 2022 15:55:08 +1100 Subject: [PATCH 051/171] Finishes FTX testing, adds comments --- exchanges/exchange.go | 14 +- exchanges/ftx/ftx.go | 2 + exchanges/ftx/ftx_test.go | 296 ++++++++++--------------------- exchanges/ftx/ftx_types.go | 2 + exchanges/ftx/ftx_wrapper.go | 52 ++++-- exchanges/interfaces.go | 2 +- exchanges/order/futures.go | 28 +-- exchanges/order/futures_types.go | 20 +-- 8 files changed, 159 insertions(+), 257 deletions(-) diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 9b8f287ac1b..ab120dd5234 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1465,25 +1465,25 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ // CalculatePNL is an overridable function to allow PNL to be calculated on an // open position // It will also determine whether the position is considered to be liquidated -// for live trading, an overrided function may wish to confirm the liquidation by +// For live trading, an overrided function may wish to confirm the liquidation by // requesting the status of the asset func (b *Base) CalculatePNL(*order.PNLCalculatorRequest) (*order.PNLResult, error) { return nil, common.ErrNotYetImplemented } -// ScaleCollateral is an overridable function to allow PNL to be calculated on an -// open position -// It will also determine whether the position is considered to be liquidated -// for live trading, an overrided function may wish to confirm the liquidation by -// requesting the status of the asset +// ScaleCollateral is an overridable function to determine how much +// collateral is usable in futures positions func (b *Base) ScaleCollateral(*order.CollateralCalculator) (decimal.Decimal, error) { return decimal.Zero, common.ErrNotYetImplemented } +// CalculateTotalCollateral takes in n collateral calculators to determine an overall +// standing in a singular currency. See FTX's implementation func (b *Base) CalculateTotalCollateral([]order.CollateralCalculator) (decimal.Decimal, error) { return decimal.Zero, common.ErrNotYetImplemented } -func (b *Base) GetFuturesPositions(asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) { +// GetFuturesPositions returns futures positions according to the provided parameters +func (b *Base) GetFuturesPositions(context.Context, asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 7a36059315c..a351c6873b3 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -140,6 +140,8 @@ var ( errSubaccountTransferSourceDestinationMustNotBeEqual = errors.New("subaccount transfer source and destination must not be the same value") errUnrecognisedOrderStatus = errors.New("unrecognised order status received") errInvalidOrderAmounts = errors.New("filled amount should not exceed order amount") + errCollateralCurrencyNotFound = errors.New("no collateral scaling information found") + errCollateralIMFMissing = errors.New("cannot scale collateral, missing IMF information") validResolutionData = []int64{15, 60, 300, 900, 3600, 14400, 86400} ) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 48385c26778..84567c9c0c1 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -2,12 +2,11 @@ package ftx import ( "context" - "encoding/json" "errors" "fmt" "log" + "math" "os" - "sort" "sync" "testing" "time" @@ -1730,110 +1729,11 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { } } -func TestCalculatePNLFromOrders(t *testing.T) { - pressXToJSON := `{"success":true,"result":[ - {"id":102360167561,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49318.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:31:09.246156+00:00","future":"BTC-1231"}, - {"id":102359914432,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49311.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:30:02.546539+00:00","future":"BTC-1231"}, - {"id":102359875376,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49289.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:29:48.461182+00:00","future":"BTC-1231"}, - {"id":102357575195,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49205.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:17:35.686416+00:00","future":"BTC-1231"}, - {"id":102356768854,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0009,"status":"closed","filledSize":0.0009,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49248.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T04:13:28.891973+00:00","future":"BTC-1231"}, - {"id":102334270248,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49068.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:41.551614+00:00","future":"BTC-1231"}, - {"id":102334231437,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0004,"status":"closed","filledSize":0.0004,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49050.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:27.615861+00:00","future":"BTC-1231"}, - {"id":102333965786,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0003,"status":"closed","filledSize":0.0003,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49030.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:19:04.483189+00:00","future":"BTC-1231"}, - {"id":102333762284,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49061.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:07.703362+00:00","future":"BTC-1231"}, - {"id":102333732631,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49063.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:02.044371+00:00","future":"BTC-1231"}, - {"id":102321616290,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":48637.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:35.419340+00:00","future":"BTC-1231"}, - {"id":102321498868,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48624.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:28:07.428170+00:00","future":"BTC-1231"}, - {"id":102321095447,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48633.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:26:34.077562+00:00","future":"BTC-1231"}, - {"id":102320767608,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":48720.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T01:25:32.920109+00:00","future":"BTC-1231"}, - {"id":101062637645,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0002,"status":"closed","filledSize":0.0002,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":57487.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:45:32.636504+00:00","future":"BTC-1231"}, - {"id":101062454431,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:44.588077+00:00","future":"BTC-1231"}, - {"id":101062444084,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":57561.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-02T01:44:40.982596+00:00","future":"BTC-1231"}, - {"id":92221164777,"clientId":null,"market":"BTC-1231","type":"market","side":"buy","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":61679.0,"postOnly":false,"ioc":true,"createdAt":"2021-11-01T02:25:43.913874+00:00","future":"BTC-1231"}, - {"id":92186217479,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":62902.0,"postOnly":false,"ioc":true,"createdAt":"2021-10-31T23:38:26.914224+00:00","future":"BTC-1231"} -],"hasMoreData":false}` - resp := struct { - Data []OrderData `json:"result"` - }{} - err := json.Unmarshal([]byte(pressXToJSON), &resp) - if err != nil { - t.Fatal() - } - result := resp.Data - sort.Slice(result, func(i, j int) bool { - return result[i].CreatedAt.Before(result[j].CreatedAt) - }) - - pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) - var orders []order.Detail - for i := range result { - price := result[i].AvgFillPrice - side, err := order.StringToOrderSide(result[i].Side) - if err != nil { - t.Error(err) - } - orders = append(orders, order.Detail{ - Side: side, - Pair: pair, - ID: fmt.Sprintf("%v", result[i].ID), - Price: price, - Amount: result[i].Size, - Status: order.Status(result[i].Status), - AssetType: asset.Futures, - Exchange: f.Name, - Date: result[i].CreatedAt, - }) - } - - exch := f.Name - item := asset.Futures - setup := &order.PositionControllerSetup{ - Exchange: exch, - Asset: item, - Pair: pair, - Underlying: pair.Base, - ExchangePNLCalculation: &f, - UseExchangePNLCalculation: false, - } - p, err := order.SetupMultiPositionTracker(setup) - if err != nil { - t.Error(err) - } - for i := range orders { - err = p.TrackNewOrder(&orders[i]) - if err != nil { - t.Error(err) - } - } - pos := p.GetPositions() - if len(pos) != 6 { - t.Fatal("expected 6 positions") - } - if pnl := pos[0].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.1223)) { - t.Errorf("expected nil err, received '%v', expected 0.1223, received '%v'", err, pnl) - } - if pnl := pos[1].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0148)) { - t.Errorf("expected nil err, received '%v', expected 0.0148, received '%v'", err, pnl) - } - if pnl := pos[2].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0004)) { - t.Errorf("expected nil err, received '%v', expected 0.0092, received '%v'", err, pnl) - } - if pnl := pos[3].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0098)) { - t.Errorf("expected nil err, received '%v', expected -0.0054, received '%v'", err, pnl) - } - if pnl := pos[4].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(0.0387)) { - t.Errorf("expected nil err, received '%v', expected 0.0387, received '%v'", err, pnl) - } - if pnl := pos[5].GetRealisedPNL(); !pnl.Equal(decimal.NewFromFloat(-0.0029)) { - t.Errorf("expected nil err, received '%v', expected -0.0029, received '%v'", err, pnl) - } -} - func TestScaleCollateral(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - ai, err := f.GetAccountInfo(context.Background()) + accountInfo, err := f.GetAccountInfo(context.Background()) if err != nil { t.Error(err) } @@ -1841,23 +1741,25 @@ func TestScaleCollateral(t *testing.T) { if err != nil { t.Error(err) } - myCollat := 0.0 - liquidationVersion := 0.0 - usdHo := 0.0 + localScaling := 0.0 + liquidationScaling := 0.0 + providedUSDValue := 0.0 for _, v := range walletInfo { for v2 := range v { coin := v[v2].Coin if coin == "USD" { - myCollat += v[v2].Total - usdHo += v[v2].USDValue - liquidationVersion += v[v2].Total + localScaling += v[v2].Total + providedUSDValue += v[v2].USDValue + liquidationScaling += v[v2].Total continue } - tick, err := f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) + var tick MarketData + tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) if err != nil { t.Error(err) } - scaled, err := f.ScaleCollateral(&order.CollateralCalculator{ + var scaled decimal.Decimal + scaled, err = f.ScaleCollateral(&order.CollateralCalculator{ CollateralCurrency: currency.NewCode(coin), Asset: asset.Spot, Side: order.Buy, @@ -1865,10 +1767,13 @@ func TestScaleCollateral(t *testing.T) { USDPrice: decimal.NewFromFloat(tick.Price), }) if err != nil { + if errors.Is(err, errCollateralCurrencyNotFound) { + continue + } t.Error(err) } - myCollat += scaled.InexactFloat64() - usdHo += v[v2].USDValue + localScaling += scaled.InexactFloat64() + providedUSDValue += v[v2].USDValue scaled, err = f.ScaleCollateral(&order.CollateralCalculator{ CollateralCurrency: currency.NewCode(coin), @@ -1882,114 +1787,84 @@ func TestScaleCollateral(t *testing.T) { t.Error(err) } - liquidationVersion += scaled.InexactFloat64() + liquidationScaling += scaled.InexactFloat64() } } - t.Logf("%v collateral", ai.Collateral) - t.Logf("%v my calcs", myCollat) - t.Logf("%v liquidation calcs", liquidationVersion) - t.Logf("%v usd total", usdHo) + if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { + t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) + } } -func TestCalculatePNLFromOrders1(t *testing.T) { +func TestCalculateTotalCollateral(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - resp, err := f.GetFills(context.Background(), spotPair, "200", time.Time{}, time.Time{}) - result := resp - sort.Slice(result, func(i, j int) bool { - return result[i].Time.Before(result[j].Time) - }) - - pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) - var orders []order.Detail - for i := range result { - price := result[i].Price - side, err := order.StringToOrderSide(result[i].Side) - if err != nil { - t.Error(err) - } - orders = append(orders, order.Detail{ - Side: side, - Pair: pair, - ID: fmt.Sprintf("%v", result[i].ID), - Price: price, - Amount: result[i].Size, - AssetType: asset.Futures, - Exchange: f.Name, - Fee: result[i].Fee, - Date: result[i].Time, - }) + walletInfo, err := f.GetAllWalletBalances(context.Background()) + if err != nil { + t.Error(err) } - - exch := f.Name - item := asset.Futures - setup := &order.PositionControllerSetup{ - Exchange: exch, - Asset: item, - Pair: pair, - Underlying: pair.Base, - //ExchangePNLCalculation: &f, - //UseExchangePNLCalculation: true, - //OfflineCalculation: true, + var scales []order.CollateralCalculator + for _, v := range walletInfo { + for v2 := range v { + coin := v[v2].Coin + if coin == "USD" { + total := decimal.NewFromFloat(v[v2].Total) + scales = append(scales, order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: total, + USDPrice: total, + }) + continue + } + var tick MarketData + tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) + if err != nil { + t.Error(err) + } + scales = append(scales, order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + CollateralAmount: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + }) + } } - p, err := order.SetupMultiPositionTracker(setup) + total, err := f.CalculateTotalCollateral(scales) if err != nil { t.Error(err) } - for i := range orders { - err = p.TrackNewOrder(&orders[i]) - if err != nil { - t.Error(err) - } + localScaling := total.InexactFloat64() + accountInfo, err := f.GetAccountInfo(context.Background()) + if err != nil { + t.Error(err) } - positions := p.GetPositions() - for i := range positions { - _, err = positions[i].GetLatestPNLSnapshot() - if err != nil { - t.Error(err) - } + if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { + t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } } -func TestCalculatePNLFromOrders3(t *testing.T) { - pressXToJSON := `{"success":true,"result":[ - {"id":102334270248,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":true,"liquidation":false,"avgFillPrice":49068.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:41.551614+00:00","future":"BTC-1231"}, - {"id":102334231437,"clientId":null,"market":"BTC-1231","type":"market","side":"sell","price":null,"size":0.0004,"status":"closed","filledSize":0.0004,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49050.0,"postOnly":false,"ioc":true,"createdAt":"2021-12-06T02:20:27.615861+00:00","future":"BTC-1231"}, - {"id":102333965786,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0003,"status":"closed","filledSize":0.0003,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49030.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:19:04.483189+00:00","future":"BTC-1231"}, - {"id":102333762284,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49061.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:07.703362+00:00","future":"BTC-1231"}, - {"id":102333732631,"clientId":null,"market":"BTC-1231","type":"limit","side":"buy","price":49072.0,"size":0.0001,"status":"closed","filledSize":0.0001,"remainingSize":0.0,"reduceOnly":false,"liquidation":false,"avgFillPrice":49063.0,"postOnly":false,"ioc":false,"createdAt":"2021-12-06T02:18:02.044371+00:00","future":"BTC-1231"} -],"hasMoreData":false}` - resp := struct { - Data []OrderData `json:"result"` - }{} - err := json.Unmarshal([]byte(pressXToJSON), &resp) - if err != nil { - t.Fatal() +func TestCalculatePNL(t *testing.T) { + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") } - result := resp.Data - sort.Slice(result, func(i, j int) bool { - return result[i].CreatedAt.Before(result[j].CreatedAt) - }) - + f.Verbose = true pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) + positions, err := f.GetFuturesPositions(context.Background(), asset.Futures, pair, time.Date(2021, 1, 6, 4, 28, 0, 0, time.UTC), time.Date(2021, 12, 31, 4, 32, 0, 0, time.UTC)) var orders []order.Detail - for i := range result { - price := result[i].AvgFillPrice - side, err := order.StringToOrderSide(result[i].Side) - if err != nil { - t.Error(err) - } + for i := range positions { orders = append(orders, order.Detail{ - Side: side, + Side: positions[i].Side, Pair: pair, - ID: fmt.Sprintf("%v", result[i].ID), - Price: price, - Amount: result[i].Size, - Status: order.Status(result[i].Status), + ID: fmt.Sprintf("%v", positions[i].ID), + Price: positions[i].Price, + Amount: positions[i].Amount, AssetType: asset.Futures, Exchange: f.Name, - Date: result[i].CreatedAt, + Fee: positions[i].Fee, + Date: positions[i].Date, }) } @@ -2000,9 +1875,8 @@ func TestCalculatePNLFromOrders3(t *testing.T) { Asset: item, Pair: pair, Underlying: pair.Base, - ExchangePNLCalculation: &f, UseExchangePNLCalculation: true, - OfflineCalculation: true, + ExchangePNLCalculation: &f, } p, err := order.SetupMultiPositionTracker(setup) if err != nil { @@ -2014,7 +1888,25 @@ func TestCalculatePNLFromOrders3(t *testing.T) { t.Error(err) } } - pos := p.GetPositions() - pnl := pos[0].GetRealisedPNL() - t.Logf("%v", pnl) + results := p.GetPositions() + for i := range results { + _, err = results[i].GetLatestPNLSnapshot() + if err != nil { + t.Error(err) + } + } +} + +func TestGetFuturesPositions(t *testing.T) { + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + cp := currency.NewPair(currency.BTC, currency.NewCode("1231")) + start := time.Now().Add(-time.Hour * 24 * 365) + end := time.Now() + a := asset.Futures + _, err := f.GetFuturesPositions(context.Background(), a, cp, start, end) + if err != nil { + t.Error(err) + } } diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index 9ebac31f5ab..1634851ac3f 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -889,6 +889,8 @@ type StakeReward struct { // CollateralWeightHolder stores collateral weights over the lifecycle of the application type CollateralWeightHolder map[string]CollateralWeight +// CollateralWeight holds collateral information provided by FTX +// it is used to scale collateral when the currency is not in USD type CollateralWeight struct { Initial float64 Total float64 diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 9dbb8793fbe..6abdc9d9655 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -818,7 +818,7 @@ func (s *OrderData) GetCompatible(ctx context.Context, f *FTX) (OrderVars, error } // GetOrderInfo returns order information based on order ID -func (f *FTX) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) { +func (f *FTX) GetOrderInfo(ctx context.Context, orderID string, _ currency.Pair, _ asset.Item) (order.Detail, error) { var resp order.Detail orderData, err := f.GetOrderStatus(ctx, orderID) if err != nil { @@ -1273,7 +1273,7 @@ func (f *FTX) GetAvailableTransferChains(ctx context.Context, cryptocurrency cur return availableChains, nil } -// CalculatePNL uses a high-tech algorithm to calculate your pnl +// CalculatePNL determines the PNL of a given position based on the PNLCalculatorRequest func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, error) { if pnl == nil { return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) @@ -1281,12 +1281,9 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e var result order.PNLResult result.Time = pnl.Time if pnl.CalculateOffline { - result.Fee = pnl.Fee - result.UnrealisedPNL = pnl.Amount.Mul(pnl.CurrentPrice.Sub(pnl.PreviousPrice)) - result.Price = pnl.CurrentPrice - ftxPNLCalculation := pnl.Amount.Mul(pnl.CurrentPrice.Sub(pnl.EntryPrice)) - result.RealisedPNLBeforeFees = ftxPNLCalculation.Sub(result.UnrealisedPNL) - return &result, nil + // PNLCalculator matches FTX's pnl calculation method + calc := order.PNLCalculator{} + return calc.CalculatePNL(pnl) } ep := pnl.EntryPrice.InexactFloat64() @@ -1295,35 +1292,44 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e return nil, err } if info.Liquidating || info.Collateral == 0 { + result.IsLiquidated = true return &result, order.ErrPositionLiquidated } for i := range info.Positions { - pair, err := currency.NewPairFromString(info.Positions[i].Future) + var pair currency.Pair + pair, err = currency.NewPairFromString(info.Positions[i].Future) if err != nil { return nil, err } if !pnl.Pair.Equal(pair) { continue } - if info.Positions[i].EntryPrice == ep { - result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) - result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) - result.Price = decimal.NewFromFloat(info.Positions[i].Cost) + if info.Positions[i].EntryPrice != ep { + continue } + result.UnrealisedPNL = decimal.NewFromFloat(info.Positions[i].UnrealizedPNL) + result.RealisedPNLBeforeFees = decimal.NewFromFloat(info.Positions[i].RealizedPNL) + result.Price = decimal.NewFromFloat(info.Positions[i].Cost) + return &result, nil } - return &result, nil + // order no longer active, use offline calculation + pnl.CalculateOffline = true + return f.CalculatePNL(pnl) } // ScaleCollateral takes your totals and scales them according to FTX's rules func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal + if calc.CollateralCurrency == currency.USD { + return calc.CollateralAmount, nil + } collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] if !ok { - return decimal.Zero, errCoinMustBeSpecified + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) } if calc.CollateralAmount.IsPositive() { if collateralWeight.IMFFactor == 0 { - return decimal.Zero, errCoinMustBeSpecified + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralIMFMissing) } var scaling decimal.Decimal if calc.IsLiquidating { @@ -1339,12 +1345,16 @@ func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal return result, nil } -// CalculateTotalCollateral scales collateral and determines how much you have in USD (maybe) +// CalculateTotalCollateral scales collateral and determines how much collateral you can use for positions func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal for i := range collateralAssets { collateral, err := f.ScaleCollateral(&collateralAssets[i]) if err != nil { + if errors.Is(err, errCollateralCurrencyNotFound) { + log.Error(log.ExchangeSys, err) + continue + } return decimal.Zero, err } result = result.Add(collateral) @@ -1352,14 +1362,18 @@ func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalcul return result, nil } -func (f *FTX) GetFuturesPositions(a asset.Item, cp currency.Pair, start, end time.Time) ([]order.Detail, error) { +// GetFuturesPositions returns all futures positions within provided params +func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency.Pair, start, end time.Time) ([]order.Detail, error) { if !a.IsFutures() { return nil, fmt.Errorf("%w futures asset type only", common.ErrFunctionNotSupported) } - fills, err := f.GetFills(context.Background(), cp, "200", start, end) + fills, err := f.GetFills(ctx, cp, "200", start, end) if err != nil { return nil, err } + sort.Slice(fills, func(i, j int) bool { + return fills[i].Time.Before(fills[j].Time) + }) var resp []order.Detail var side order.Side for i := range fills { diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index d80cd7fa5c5..70cf3b74fab 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -84,7 +84,7 @@ type IBotExchange interface { order.PNLCalculation order.CollateralManagement - GetFuturesPositions(asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) + GetFuturesPositions(context.Context, asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) GetWebsocket() (*stream.Websocket, error) IsWebsocketEnabled() bool diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 62e0816ff76..f4f15317aa2 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -409,31 +409,33 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if err != nil { return err } - if cal.OrderDirection.IsLong() { cal.OrderDirection = Short } else if cal.OrderDirection.IsShort() { cal.OrderDirection = Long } - if cal.OpeningDirection.IsLong() { - cal.OpeningDirection = Short - } else if cal.OpeningDirection.IsShort() { - cal.OpeningDirection = Long + if p.openingDirection.IsLong() { + p.openingDirection = Short + } else if p.openingDirection.IsShort() { + p.openingDirection = Long } + cal.Amount = second cal.EntryPrice = price cal.Time = cal.Time.Add(1) + cal.PNLHistory = p.pnlHistory result, err = p.PNLCalculation.CalculatePNL(cal) + } else { result, err = p.PNLCalculation.CalculatePNL(cal) - if err != nil { - if !errors.Is(err, ErrPositionLiquidated) { - return err - } - result.UnrealisedPNL = decimal.Zero - result.RealisedPNLBeforeFees = decimal.Zero - p.status = Closed + } + if err != nil { + if !errors.Is(err, ErrPositionLiquidated) { + return err } + result.UnrealisedPNL = decimal.Zero + result.RealisedPNLBeforeFees = decimal.Zero + p.status = Closed } p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) if err != nil { @@ -495,8 +497,8 @@ func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, er } else if calc.OpeningDirection.IsShort() { if previousPNL != nil { first = previousPNL.Price - second = calc.CurrentPrice } + second = calc.CurrentPrice } switch { case calc.OpeningDirection.IsShort() && calc.OrderDirection.IsShort(), diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index df7f263546c..f7109fda484 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -151,6 +151,9 @@ type CollateralCalculator struct { IsLiquidating bool } +// PNLCalculator implements the PNLCalculation interface +// to call CalculatePNL and is used when a user wishes to have a +// consistent method of calculating PNL across different exchanges type PNLCalculator struct{} // PNLCalculatorRequest is used to calculate PNL values @@ -176,21 +179,7 @@ type PNLCalculatorRequest struct { CurrentDirection Side } -// TimeBasedCalculation will update PNL values -// based on the current time -type TimeBasedCalculation struct { - Time time.Time - CurrentPrice float64 -} - -// ExchangeBasedCalculation are the fields required to -// calculate PNL using an exchange's custom PNL calculations -// eg FTX uses a different method than Binance to calculate PNL -// values -type ExchangeBasedCalculation struct { -} - -// PNLResult stores pnl history at a point in time +// PNLResult stores a PNL result from a point in time type PNLResult struct { Time time.Time UnrealisedPNL decimal.Decimal @@ -198,6 +187,7 @@ type PNLResult struct { Price decimal.Decimal Exposure decimal.Decimal Fee decimal.Decimal + IsLiquidated bool } // PositionStats is a basic holder From 8c4e2f425e79a4af601a2a941e69f8f3ca6e55b4 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 5 Jan 2022 17:07:55 +1100 Subject: [PATCH 052/171] Exposes collateral calculations to rpc --- cmd/gctcli/commands.go | 132 +- cmd/gctcli/helpers.go | 17 + engine/order_manager.go | 46 +- engine/rpcserver.go | 68 +- exchanges/exchange.go | 6 +- exchanges/ftx/ftx_test.go | 30 +- exchanges/ftx/ftx_wrapper.go | 79 +- exchanges/order/futures.go | 5 +- exchanges/order/futures_types.go | 22 +- gctrpc/rpc.pb.go | 2146 +++++++++++++++++------------- gctrpc/rpc.pb.gw.go | 83 ++ gctrpc/rpc.proto | 26 +- gctrpc/rpc.swagger.json | 82 ++ gctrpc/rpc_grpc.pb.go | 36 + 14 files changed, 1709 insertions(+), 1069 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index cd9d066903d..5a7f883b35d 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4723,31 +4723,63 @@ func findMissingSavedCandleIntervals(c *cli.Context) error { return nil } -// negateLocalOffset helps negate the offset of time generation -// when the unix time gets to rpcserver, it no longer is the same time -// that was sent as it handles it as a UTC value, even though when -// using starttime it is generated as your local time -// eg 2020-01-01 12:00:00 +10 will convert into -// 2020-01-01 12:00:00 +00 when at RPCServer -// so this function will minus the offset from the local sent time -// to allow for proper use at RPCServer -func negateLocalOffset(t time.Time) string { - _, offset := time.Now().Zone() - loc := time.FixedZone("", -offset) - - return t.In(loc).Format(common.SimpleTimeFormat) +var getFuturesPositionsCommand = &cli.Command{ + Name: "getfuturesposition", + Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened", + ArgsUsage: " ", + Action: getFuturesPositions, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "exchange", + Aliases: []string{"e"}, + Usage: "the exchange to retrieve futures positions from", + }, + &cli.StringFlag{ + Name: "asset", + Aliases: []string{"a"}, + Usage: "the asset type of the currency pair, must be a futures type", + }, + &cli.StringFlag{ + Name: "pair", + Aliases: []string{"p"}, + Usage: "the currency pair", + }, + &cli.StringFlag{ + Name: "start", + Aliases: []string{"sd"}, + Usage: " rounded down to the nearest hour, ensure your starting position is within this window for accurate calculations", + Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat), + Destination: &startTime, + }, + &cli.StringFlag{ + Name: "end", + Aliases: []string{"ed"}, + Usage: " rounded down to the nearest hour, ensure your last position is within this window for accurate calculations", + Value: time.Now().Truncate(time.Hour).Format(common.SimpleTimeFormat), + Destination: &endTime, + }, + &cli.IntFlag{ + Name: "limit", + Aliases: []string{"l"}, + Usage: "the number of positions (not orders) to return", + Value: 86400, + Destination: &limit, + }, + &cli.StringFlag{ + Name: "status", + Aliases: []string{"s"}, + Usage: "limit return to position statuses - open, closed, any", + Value: "ANY", + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "includes all orders that make up a position in the response", + }, + }, } func getFuturesPositions(c *cli.Context) error { - /* - Exchange - Asset - Pair - StartDate - EndDate - Status - PositionLimit - */ if c.NArg() == 0 && c.NumFlags() == 0 { return cli.ShowCommandHelp(c, "getfuturesposition") } @@ -4873,59 +4905,3 @@ func getFuturesPositions(c *cli.Context) error { jsonOutput(result) return nil } - -var getFuturesPositionsCommand = &cli.Command{ - Name: "getfuturesposition", - Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened", - ArgsUsage: " ", - Action: getFuturesPositions, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "exchange", - Aliases: []string{"e"}, - Usage: "the exchange to find the missing candles", - }, - &cli.StringFlag{ - Name: "asset", - Aliases: []string{"a"}, - Usage: "the asset type of the currency pair", - }, - &cli.StringFlag{ - Name: "pair", - Aliases: []string{"p"}, - Usage: "the currency pair", - }, - &cli.StringFlag{ - Name: "start", - Aliases: []string{"sd"}, - Usage: " rounded down to the nearest hour", - Value: time.Now().AddDate(-1, 0, 0).Truncate(time.Hour).Format(common.SimpleTimeFormat), - Destination: &startTime, - }, - &cli.StringFlag{ - Name: "end", - Aliases: []string{"ed"}, - Usage: " rounded down to the nearest hour", - Value: time.Now().Truncate(time.Hour).Format(common.SimpleTimeFormat), - Destination: &endTime, - }, - &cli.IntFlag{ - Name: "limit", - Aliases: []string{"l"}, - Usage: "the number of positions (not orders) to return", - Value: 86400, - Destination: &limit, - }, - &cli.StringFlag{ - Name: "status", - Aliases: []string{"s"}, - Usage: "limit return to position statuses - open, closed, any", - Value: "ANY", - }, - &cli.BoolFlag{ - Name: "verbose", - Aliases: []string{"v"}, - Usage: "includes all orders in the response", - }, - }, -} diff --git a/cmd/gctcli/helpers.go b/cmd/gctcli/helpers.go index 92bc2d0160a..dfe4567e5cb 100644 --- a/cmd/gctcli/helpers.go +++ b/cmd/gctcli/helpers.go @@ -6,7 +6,9 @@ import ( "os" "os/exec" "runtime" + "time" + "github.com/thrasher-corp/gocryptotrader/common" "google.golang.org/grpc" ) @@ -31,3 +33,18 @@ func closeConn(conn *grpc.ClientConn, cancel context.CancelFunc) { cancel() } } + +// negateLocalOffset helps negate the offset of time generation +// when the unix time gets to rpcserver, it no longer is the same time +// that was sent as it handles it as a UTC value, even though when +// using starttime it is generated as your local time +// eg 2020-01-01 12:00:00 +10 will convert into +// 2020-01-01 12:00:00 +00 when at RPCServer +// so this function will minus the offset from the local sent time +// to allow for proper use at RPCServer +func negateLocalOffset(t time.Time) string { + _, offset := time.Now().Zone() + loc := time.FixedZone("", -offset) + + return t.In(loc).Format(common.SimpleTimeFormat) +} diff --git a/engine/order_manager.go b/engine/order_manager.go index abe29fb4b2b..52f97b9bf4b 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -225,6 +225,19 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { return nil } +// GetFuturesPositionsForExchange returns futures positions stored within +// the order manager's futures position tracker that match the provided params +func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*order.PositionTracker, error) { + if m == nil { + return nil, fmt.Errorf("order manager %w", ErrNilSubsystem) + } + if atomic.LoadInt32(&m.started) == 0 { + return nil, fmt.Errorf("order manager %w", ErrSubSystemNotStarted) + } + + return m.orderStore.futuresPositionTrackers.GetPositionsForExchange(exch, item, pair) +} + // GetOrderInfo calls the exchange's wrapper GetOrderInfo function // and stores the result in the order manager func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID string, cp currency.Pair, a asset.Item) (order.Detail, error) { @@ -839,7 +852,9 @@ func (s *store) updateExisting(od *order.Detail) error { if r[x].AssetType.IsFutures() { err := s.futuresPositionTrackers.TrackNewOrder(r[x]) if err != nil { - return err + if !errors.Is(err, order.ErrPositionClosed) { + return err + } } } return nil @@ -864,7 +879,9 @@ func (s *store) modifyExisting(id string, mod *order.Modify) error { if r[x].AssetType.IsFutures() { err := s.futuresPositionTrackers.TrackNewOrder(r[x]) if err != nil { - return err + if !errors.Is(err, order.ErrPositionClosed) { + return err + } } } return nil @@ -886,6 +903,14 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) } s.m.Lock() defer s.m.Unlock() + if od.AssetType.IsFutures() { + err = s.futuresPositionTrackers.TrackNewOrder(od) + if err != nil { + if !errors.Is(err, order.ErrPositionClosed) { + return nil, err + } + } + } r, ok := s.Orders[lName] if !ok { od.GenerateInternalOrderID() @@ -903,12 +928,6 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) OrderDetails: r[x].Copy(), IsNewOrder: false, } - if od.AssetType.IsFutures() { - err = s.futuresPositionTrackers.TrackNewOrder(od) - if err != nil { - return nil, err - } - } return resp, nil } } @@ -919,12 +938,6 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) OrderDetails: od.Copy(), IsNewOrder: true, } - if od.AssetType.IsFutures() { - err = s.futuresPositionTrackers.TrackNewOrder(od) - if err != nil { - return nil, err - } - } return resp, nil } @@ -995,7 +1008,10 @@ func (s *store) add(det *order.Detail) error { s.Orders[strings.ToLower(det.Exchange)] = orders if det.AssetType.IsFutures() { - return s.futuresPositionTrackers.TrackNewOrder(det) + err = s.futuresPositionTrackers.TrackNewOrder(det) + if err != nil { + return err + } } return nil } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 941fbc85048..a0b1cb4d593 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -19,6 +19,7 @@ import ( grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/pquerna/otp/totp" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/common/file" @@ -4109,7 +4110,7 @@ func (s *RPCServer) CurrencyStateTradingPair(_ context.Context, r *gctrpc.Curren } // GetFuturesPositions returns pnl positions for an exchange asset pair -func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesPositionsRequest) (*gctrpc.GetFuturesPositionsResponse, error) { +func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuturesPositionsRequest) (*gctrpc.GetFuturesPositionsResponse, error) { exch, err := s.GetExchangeByName(r.Exchange) if err != nil { return nil, err @@ -4142,7 +4143,7 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP return nil, err } - orders, err := exch.GetFuturesPositions(a, cp, start, end) + orders, err := exch.GetFuturesPositions(ctx, a, cp, start, end) if err != nil { return nil, err } @@ -4150,14 +4151,14 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP return orders[i].Date.Before(orders[j].Date) }) for i := range orders { - err = s.OrderManager.orderStore.futuresPositionTrackers.TrackNewOrder(&orders[i]) + _, err = s.OrderManager.UpsertOrder(&orders[i]) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { return nil, err } } } - pos, err := s.OrderManager.orderStore.futuresPositionTrackers.GetPositionsForExchange(r.Exchange, a, cp) + pos, err := s.OrderManager.GetFuturesPositionsForExchange(r.Exchange, a, cp) if err != nil { return nil, err } @@ -4225,3 +4226,62 @@ func (s *RPCServer) GetFuturesPositions(_ context.Context, r *gctrpc.GetFuturesP response.TotalPNL = response.TotalRealisedPNL + response.TotalUnrealisedPNL return response, nil } + +// GetCollateral returns pnl positions for an exchange asset pair +func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRequest) (*gctrpc.GetCollateralResponse, error) { + exch, err := s.GetExchangeByName(r.Exchange) + if err != nil { + return nil, err + } + + a := asset.Item(r.Asset) + err = checkParams(r.Exchange, exch, a, currency.Pair{}) + if err != nil { + return nil, err + } + ai, err := exch.FetchAccountInfo(ctx.a) + if err != nil { + return nil, err + } + + var calculators []order.CollateralCalculator + var acc account.SubAccount + if r.SubAccount != "" { + for i := range ai.Accounts { + if strings.EqualFold(r.SubAccount, ai.Accounts[i].ID) { + acc = ai.Accounts[i] + break + + } + } + } else if len(ai.Accounts) > 0 { + acc = ai.Accounts[0] + } + for j := range acc.Currencies { + calculators = append(calculators, order.CollateralCalculator{ + CalculateOffline: r.CalculateOffline, + CollateralCurrency: ai.Accounts[i].Currencies[j].CurrencyName, + Asset: a, + CollateralAmount: decimal.NewFromFloat(ai.Accounts[i].Currencies[j].TotalValue), + }) + } + + collateral, err := exch.CalculateTotalCollateral(ctx, calculators) + if err != nil { + return nil, err + } + + result := &gctrpc.GetCollateralResponse{ + TotalCollateral: collateral.TotalCollateral.String(), + } + if r.IncludeBreakdown { + for i := range collateral.BreakdownByCurrency { + result.CurrencyBreakdown = append(result.CurrencyBreakdown, &gctrpc.CollateralForCurrency{ + Currency: collateral.BreakdownByCurrency[i].Currency.String(), + ScaledCollateral: collateral.BreakdownByCurrency[i].Amount.String(), + ScaledToCurrency: collateral.BreakdownByCurrency[i].ValueCurrency.String(), + }) + } + } + return result, nil +} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index ab120dd5234..559eafb0d7d 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1473,14 +1473,14 @@ func (b *Base) CalculatePNL(*order.PNLCalculatorRequest) (*order.PNLResult, erro // ScaleCollateral is an overridable function to determine how much // collateral is usable in futures positions -func (b *Base) ScaleCollateral(*order.CollateralCalculator) (decimal.Decimal, error) { +func (b *Base) ScaleCollateral(context.Context, *order.CollateralCalculator) (decimal.Decimal, error) { return decimal.Zero, common.ErrNotYetImplemented } // CalculateTotalCollateral takes in n collateral calculators to determine an overall // standing in a singular currency. See FTX's implementation -func (b *Base) CalculateTotalCollateral([]order.CollateralCalculator) (decimal.Decimal, error) { - return decimal.Zero, common.ErrNotYetImplemented +func (b *Base) CalculateTotalCollateral(context.Context, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { + return nil, common.ErrNotYetImplemented } // GetFuturesPositions returns futures positions according to the provided parameters diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 84567c9c0c1..27f47100d12 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1759,12 +1759,13 @@ func TestScaleCollateral(t *testing.T) { t.Error(err) } var scaled decimal.Decimal - scaled, err = f.ScaleCollateral(&order.CollateralCalculator{ + scaled, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ CollateralCurrency: currency.NewCode(coin), Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), USDPrice: decimal.NewFromFloat(tick.Price), + CalculateOffline: true, }) if err != nil { if errors.Is(err, errCollateralCurrencyNotFound) { @@ -1775,19 +1776,28 @@ func TestScaleCollateral(t *testing.T) { localScaling += scaled.InexactFloat64() providedUSDValue += v[v2].USDValue - scaled, err = f.ScaleCollateral(&order.CollateralCalculator{ + scaled, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ CollateralCurrency: currency.NewCode(coin), Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), USDPrice: decimal.NewFromFloat(tick.Price), IsLiquidating: true, + CalculateOffline: true, }) if err != nil { t.Error(err) } - liquidationScaling += scaled.InexactFloat64() + + _, err = f.ScaleCollateral(context.Background(), &order.CollateralCalculator{ + CollateralCurrency: currency.NewCode(coin), + Asset: asset.Spot, + Side: order.Buy, + }) + if err != nil { + t.Error(err) + } } } if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { @@ -1815,6 +1825,7 @@ func TestCalculateTotalCollateral(t *testing.T) { Side: order.Buy, CollateralAmount: total, USDPrice: total, + CalculateOffline: true, }) continue } @@ -1829,14 +1840,15 @@ func TestCalculateTotalCollateral(t *testing.T) { Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), USDPrice: decimal.NewFromFloat(tick.Price), + CalculateOffline: true, }) } } - total, err := f.CalculateTotalCollateral(scales) + total, err := f.CalculateTotalCollateral(context.Background(), scales) if err != nil { t.Error(err) } - localScaling := total.InexactFloat64() + localScaling := total.TotalCollateral.InexactFloat64() accountInfo, err := f.GetAccountInfo(context.Background()) if err != nil { t.Error(err) @@ -1844,6 +1856,14 @@ func TestCalculateTotalCollateral(t *testing.T) { if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } + + for i := range scales { + scales[i].CalculateOffline = false + } + _, err = f.CalculateTotalCollateral(context.Background(), scales) + if err != nil { + t.Error(err) + } } func TestCalculatePNL(t *testing.T) { diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 6abdc9d9655..213cacfef11 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1318,48 +1318,77 @@ func (f *FTX) CalculatePNL(pnl *order.PNLCalculatorRequest) (*order.PNLResult, e } // ScaleCollateral takes your totals and scales them according to FTX's rules -func (f *FTX) ScaleCollateral(calc *order.CollateralCalculator) (decimal.Decimal, error) { +func (f *FTX) ScaleCollateral(ctx context.Context, calc *order.CollateralCalculator) (decimal.Decimal, error) { var result decimal.Decimal - if calc.CollateralCurrency == currency.USD { - return calc.CollateralAmount, nil + if calc.CalculateOffline { + if calc.CollateralCurrency == currency.USD { + return calc.CollateralAmount, nil + } + collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] + if !ok { + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) + } + if calc.CollateralAmount.IsPositive() { + if collateralWeight.IMFFactor == 0 { + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralIMFMissing) + } + var scaling decimal.Decimal + if calc.IsLiquidating { + scaling = decimal.NewFromFloat(collateralWeight.Total) + } else { + scaling = decimal.NewFromFloat(collateralWeight.Initial) + } + weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.IMFFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) + result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) + } else { + result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) + } + return result, nil + } + wallet, err := f.GetCoins(ctx) + if err != nil { + return decimal.Zero, err } - collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] - if !ok { - return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) + balances, err := f.GetBalances(ctx) + if err != nil { + return decimal.Zero, err } - if calc.CollateralAmount.IsPositive() { - if collateralWeight.IMFFactor == 0 { - return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralIMFMissing) + for i := range wallet { + if currency.NewCode(wallet[i].ID) != calc.CollateralCurrency { + continue } - var scaling decimal.Decimal - if calc.IsLiquidating { - scaling = decimal.NewFromFloat(collateralWeight.Total) - } else { - scaling = decimal.NewFromFloat(collateralWeight.Initial) + for j := range balances { + if currency.NewCode(balances[j].Coin) != calc.CollateralCurrency { + continue + } + scaled := wallet[i].CollateralWeight * balances[j].USDValue + result = decimal.NewFromFloat(scaled) + return result, nil } - weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.IMFFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) - result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) - } else { - result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) } - return result, nil + return decimal.Zero, fmt.Errorf("%v %w", calc.CollateralCurrency, errCollateralCurrencyNotFound) } // CalculateTotalCollateral scales collateral and determines how much collateral you can use for positions -func (f *FTX) CalculateTotalCollateral(collateralAssets []order.CollateralCalculator) (decimal.Decimal, error) { - var result decimal.Decimal +func (f *FTX) CalculateTotalCollateral(ctx context.Context, collateralAssets []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { + var result order.TotalCollateralResponse for i := range collateralAssets { - collateral, err := f.ScaleCollateral(&collateralAssets[i]) + collateral, err := f.ScaleCollateral(ctx, &collateralAssets[i]) if err != nil { if errors.Is(err, errCollateralCurrencyNotFound) { log.Error(log.ExchangeSys, err) continue } - return decimal.Zero, err + return nil, err } - result = result.Add(collateral) + result.TotalCollateral = result.TotalCollateral.Add(collateral) + result.BreakdownByCurrency = append(result.BreakdownByCurrency, order.CollateralByCurrency{ + Currency: collateralAssets[i].CollateralCurrency, + Amount: collateral, + ValueCurrency: currency.USD, + }) } - return result, nil + return &result, nil } // GetFuturesPositions returns all futures positions within provided params diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index f4f15317aa2..f5fbb861eda 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -58,6 +58,8 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return mpt.TrackNewOrder(d) } +// GetPositionsForExchange returns all positions for an +// exchange, asset pair that is stored in the position controller func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*PositionTracker, error) { if c == nil { return nil, common.ErrNilPointer @@ -222,6 +224,7 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) return resp, nil } +// GetStats returns a summary of a future position func (p *PositionTracker) GetStats() PositionStats { if p == nil { return PositionStats{} @@ -419,7 +422,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } else if p.openingDirection.IsShort() { p.openingDirection = Long } - + cal.Amount = second cal.EntryPrice = price cal.Time = cal.Time.Add(1) diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index f7109fda484..0a0ae5454dd 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -1,6 +1,7 @@ package order import ( + "context" "errors" "sync" "time" @@ -50,8 +51,24 @@ type PNLCalculation interface { // multiple ways of calculating the size of collateral // on an exchange type CollateralManagement interface { - ScaleCollateral(*CollateralCalculator) (decimal.Decimal, error) - CalculateTotalCollateral([]CollateralCalculator) (decimal.Decimal, error) + ScaleCollateral(context.Context, *CollateralCalculator) (decimal.Decimal, error) + CalculateTotalCollateral(context.Context, []CollateralCalculator) (*TotalCollateralResponse, error) +} + +// TotalCollateralResponse holds all collateral +type TotalCollateralResponse struct { + TotalCollateral decimal.Decimal + BreakdownByCurrency []CollateralByCurrency +} + +// CollateralByCurrency individual collateral contribution +// along with what the potentially scaled collateral +// currency it is represented as +// eg in FTX ValueCurrency is USD +type CollateralByCurrency struct { + Currency currency.Code + Amount decimal.Decimal + ValueCurrency currency.Code } // PositionController manages all futures orders @@ -143,6 +160,7 @@ type PositionTrackerSetup struct { // eg on FTX, the collateral is scaled depending on what // currency it is type CollateralCalculator struct { + CalculateOffline bool CollateralCurrency currency.Code Asset asset.Item Side Side diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 59e58674906..7ef71ba2121 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -11042,6 +11042,203 @@ func (x *FuturePosition) GetOrders() []*OrderDetails { return nil } +type GetCollateralRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + SubAccount string `protobuf:"bytes,3,opt,name=subAccount,proto3" json:"subAccount,omitempty"` + IncludeBreakdown bool `protobuf:"varint,4,opt,name=includeBreakdown,proto3" json:"includeBreakdown,omitempty"` + CalculateOffline bool `protobuf:"varint,5,opt,name=calculateOffline,proto3" json:"calculateOffline,omitempty"` +} + +func (x *GetCollateralRequest) Reset() { + *x = GetCollateralRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[172] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCollateralRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCollateralRequest) ProtoMessage() {} + +func (x *GetCollateralRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[172] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCollateralRequest.ProtoReflect.Descriptor instead. +func (*GetCollateralRequest) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{172} +} + +func (x *GetCollateralRequest) GetExchange() string { + if x != nil { + return x.Exchange + } + return "" +} + +func (x *GetCollateralRequest) GetAsset() string { + if x != nil { + return x.Asset + } + return "" +} + +func (x *GetCollateralRequest) GetSubAccount() string { + if x != nil { + return x.SubAccount + } + return "" +} + +func (x *GetCollateralRequest) GetIncludeBreakdown() bool { + if x != nil { + return x.IncludeBreakdown + } + return false +} + +func (x *GetCollateralRequest) GetCalculateOffline() bool { + if x != nil { + return x.CalculateOffline + } + return false +} + +type GetCollateralResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TotalCollateral string `protobuf:"bytes,1,opt,name=totalCollateral,proto3" json:"totalCollateral,omitempty"` + CurrencyBreakdown []*CollateralForCurrency `protobuf:"bytes,2,rep,name=currencyBreakdown,proto3" json:"currencyBreakdown,omitempty"` +} + +func (x *GetCollateralResponse) Reset() { + *x = GetCollateralResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[173] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCollateralResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCollateralResponse) ProtoMessage() {} + +func (x *GetCollateralResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[173] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCollateralResponse.ProtoReflect.Descriptor instead. +func (*GetCollateralResponse) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{173} +} + +func (x *GetCollateralResponse) GetTotalCollateral() string { + if x != nil { + return x.TotalCollateral + } + return "" +} + +func (x *GetCollateralResponse) GetCurrencyBreakdown() []*CollateralForCurrency { + if x != nil { + return x.CurrencyBreakdown + } + return nil +} + +type CollateralForCurrency struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` + ScaledCollateral string `protobuf:"bytes,2,opt,name=scaledCollateral,proto3" json:"scaledCollateral,omitempty"` + ScaledToCurrency string `protobuf:"bytes,3,opt,name=scaledToCurrency,proto3" json:"scaledToCurrency,omitempty"` +} + +func (x *CollateralForCurrency) Reset() { + *x = CollateralForCurrency{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[174] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CollateralForCurrency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CollateralForCurrency) ProtoMessage() {} + +func (x *CollateralForCurrency) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[174] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CollateralForCurrency.ProtoReflect.Descriptor instead. +func (*CollateralForCurrency) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{174} +} + +func (x *CollateralForCurrency) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *CollateralForCurrency) GetScaledCollateral() string { + if x != nil { + return x.ScaledCollateral + } + return "" +} + +func (x *CollateralForCurrency) GetScaledToCurrency() string { + if x != nil { + return x.ScaledToCurrency + } + return "" +} + type CancelBatchOrdersResponse_Orders struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -11053,7 +11250,7 @@ type CancelBatchOrdersResponse_Orders struct { func (x *CancelBatchOrdersResponse_Orders) Reset() { *x = CancelBatchOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[182] + mi := &file_rpc_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11066,7 +11263,7 @@ func (x *CancelBatchOrdersResponse_Orders) String() string { func (*CancelBatchOrdersResponse_Orders) ProtoMessage() {} func (x *CancelBatchOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[182] + mi := &file_rpc_proto_msgTypes[185] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11101,7 +11298,7 @@ type CancelAllOrdersResponse_Orders struct { func (x *CancelAllOrdersResponse_Orders) Reset() { *x = CancelAllOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[184] + mi := &file_rpc_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11114,7 +11311,7 @@ func (x *CancelAllOrdersResponse_Orders) String() string { func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} func (x *CancelAllOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[184] + mi := &file_rpc_proto_msgTypes[187] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12631,716 +12828,753 @@ var file_rpc_proto_rawDesc = []byte{ 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x32, 0x9a, 0x58, 0x0a, 0x0e, 0x47, 0x6f, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, - 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, - 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, - 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, - 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, - 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, - 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, - 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, + 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, + 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, + 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x12, 0x4b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, + 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, + 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, + 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, + 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, + 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, + 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, + 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, + 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, + 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, + 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, + 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, + 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, + 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, + 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, + 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, + 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, + 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, + 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, - 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, + 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, - 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, - 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, - 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, - 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, - 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, + 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, + 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, + 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, + 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, + 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, + 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, + 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, + 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, - 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, - 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, - 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, - 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, + 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, + 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, + 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, - 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, - 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, - 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, - 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, - 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, + 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, + 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, + 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, + 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, - 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, - 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, - 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, - 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, - 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, + 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, + 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, + 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, + 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, + 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, + 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, + 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, + 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, + 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, - 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, - 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, - 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, + 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, - 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, - 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, - 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, - 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, - 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, - 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, + 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, + 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, + 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, + 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, + 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, + 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, + 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, - 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, - 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, - 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, + 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, + 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, + 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, + 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, + 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, + 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, + 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, + 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, + 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, - 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, - 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, - 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, - 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, - 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, + 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, + 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, - 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, - 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, - 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, - 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, - 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, + 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, + 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, + 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, - 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, - 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, - 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, - 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, - 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, + 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, + 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, + 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, + 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, + 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, + 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, + 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, + 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, - 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, - 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, + 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, + 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, + 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -13355,7 +13589,7 @@ func file_rpc_proto_rawDescGZIP() []byte { return file_rpc_proto_rawDescData } -var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 188) +var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 191) var file_rpc_proto_goTypes = []interface{}{ (*GetInfoRequest)(nil), // 0: gctrpc.GetInfoRequest (*GetInfoResponse)(nil), // 1: gctrpc.GetInfoResponse @@ -13529,32 +13763,35 @@ var file_rpc_proto_goTypes = []interface{}{ (*GetFuturesPositionsRequest)(nil), // 169: gctrpc.GetFuturesPositionsRequest (*GetFuturesPositionsResponse)(nil), // 170: gctrpc.GetFuturesPositionsResponse (*FuturePosition)(nil), // 171: gctrpc.FuturePosition - nil, // 172: gctrpc.GetInfoResponse.SubsystemStatusEntry - nil, // 173: gctrpc.GetInfoResponse.RpcEndpointsEntry - nil, // 174: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - nil, // 175: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - nil, // 176: gctrpc.GetRPCEndpointsResponse.EndpointsEntry - nil, // 177: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - nil, // 178: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - nil, // 179: gctrpc.OnlineCoins.CoinsEntry - nil, // 180: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - nil, // 181: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - (*CancelBatchOrdersResponse_Orders)(nil), // 182: gctrpc.CancelBatchOrdersResponse.Orders - nil, // 183: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - (*CancelAllOrdersResponse_Orders)(nil), // 184: gctrpc.CancelAllOrdersResponse.Orders - nil, // 185: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - nil, // 186: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - nil, // 187: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - (*timestamppb.Timestamp)(nil), // 188: google.protobuf.Timestamp + (*GetCollateralRequest)(nil), // 172: gctrpc.GetCollateralRequest + (*GetCollateralResponse)(nil), // 173: gctrpc.GetCollateralResponse + (*CollateralForCurrency)(nil), // 174: gctrpc.CollateralForCurrency + nil, // 175: gctrpc.GetInfoResponse.SubsystemStatusEntry + nil, // 176: gctrpc.GetInfoResponse.RpcEndpointsEntry + nil, // 177: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + nil, // 178: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + nil, // 179: gctrpc.GetRPCEndpointsResponse.EndpointsEntry + nil, // 180: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + nil, // 181: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + nil, // 182: gctrpc.OnlineCoins.CoinsEntry + nil, // 183: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + nil, // 184: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + (*CancelBatchOrdersResponse_Orders)(nil), // 185: gctrpc.CancelBatchOrdersResponse.Orders + nil, // 186: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + (*CancelAllOrdersResponse_Orders)(nil), // 187: gctrpc.CancelAllOrdersResponse.Orders + nil, // 188: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + nil, // 189: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + nil, // 190: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + (*timestamppb.Timestamp)(nil), // 191: google.protobuf.Timestamp } var file_rpc_proto_depIdxs = []int32{ - 172, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry - 173, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry - 174, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - 175, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - 176, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry - 177, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - 178, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + 175, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry + 176, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry + 177, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + 178, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + 179, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry + 180, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + 181, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry 21, // 7: gctrpc.GetTickerRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 8: gctrpc.TickerResponse.pair:type_name -> gctrpc.CurrencyPair 22, // 9: gctrpc.Tickers.tickers:type_name -> gctrpc.TickerResponse @@ -13569,12 +13806,12 @@ var file_rpc_proto_depIdxs = []int32{ 33, // 18: gctrpc.GetAccountInfoResponse.accounts:type_name -> gctrpc.Account 38, // 19: gctrpc.GetPortfolioResponse.portfolio:type_name -> gctrpc.PortfolioAddress 43, // 20: gctrpc.OfflineCoins.addresses:type_name -> gctrpc.OfflineCoinSummary - 179, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry + 182, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry 42, // 22: gctrpc.GetPortfolioSummaryResponse.coin_totals:type_name -> gctrpc.Coin 42, // 23: gctrpc.GetPortfolioSummaryResponse.coins_offline:type_name -> gctrpc.Coin - 180, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + 183, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry 42, // 25: gctrpc.GetPortfolioSummaryResponse.coins_online:type_name -> gctrpc.Coin - 181, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + 184, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry 51, // 27: gctrpc.GetForexProvidersResponse.forex_providers:type_name -> gctrpc.ForexProvider 54, // 28: gctrpc.GetForexRatesResponse.forex_rates:type_name -> gctrpc.ForexRatesConversion 57, // 29: gctrpc.OrderDetails.trades:type_name -> gctrpc.TradeHistory @@ -13588,23 +13825,23 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 37: gctrpc.WhaleBombRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 38: gctrpc.CancelOrderRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 39: gctrpc.CancelBatchOrdersRequest.pair:type_name -> gctrpc.CurrencyPair - 182, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders - 184, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders + 185, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders + 187, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders 73, // 42: gctrpc.GetEventsResponse.condition_params:type_name -> gctrpc.ConditionParams 21, // 43: gctrpc.GetEventsResponse.pair:type_name -> gctrpc.CurrencyPair 73, // 44: gctrpc.AddEventRequest.condition_params:type_name -> gctrpc.ConditionParams 21, // 45: gctrpc.AddEventRequest.pair:type_name -> gctrpc.CurrencyPair 79, // 46: gctrpc.DepositAddresses.addresses:type_name -> gctrpc.DepositAddress - 186, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + 189, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry 94, // 48: gctrpc.WithdrawalEventByIDResponse.event:type_name -> gctrpc.WithdrawalEventResponse 94, // 49: gctrpc.WithdrawalEventsByExchangeResponse.event:type_name -> gctrpc.WithdrawalEventResponse 95, // 50: gctrpc.WithdrawalEventResponse.exchange:type_name -> gctrpc.WithdrawlExchangeEvent 96, // 51: gctrpc.WithdrawalEventResponse.request:type_name -> gctrpc.WithdrawalRequestEvent - 188, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp - 188, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp + 191, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp + 191, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp 97, // 54: gctrpc.WithdrawalRequestEvent.fiat:type_name -> gctrpc.FiatWithdrawalEvent 98, // 55: gctrpc.WithdrawalRequestEvent.crypto:type_name -> gctrpc.CryptoWithdrawalEvent - 187, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + 190, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry 21, // 57: gctrpc.SetExchangePairRequest.pairs:type_name -> gctrpc.CurrencyPair 21, // 58: gctrpc.GetOrderbookStreamRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 59: gctrpc.GetTickerStreamRequest.pair:type_name -> gctrpc.CurrencyPair @@ -13637,212 +13874,215 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 86: gctrpc.GetFuturesPositionsRequest.pair:type_name -> gctrpc.CurrencyPair 171, // 87: gctrpc.GetFuturesPositionsResponse.positions:type_name -> gctrpc.FuturePosition 56, // 88: gctrpc.FuturePosition.orders:type_name -> gctrpc.OrderDetails - 9, // 89: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 3, // 90: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer - 9, // 91: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 18, // 92: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 44, // 93: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary - 45, // 94: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins - 46, // 95: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins - 183, // 96: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - 185, // 97: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - 80, // 98: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses - 18, // 99: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 0, // 100: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest - 6, // 101: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest - 5, // 102: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 5, // 103: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 8, // 104: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest - 2, // 105: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest - 12, // 106: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest - 11, // 107: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 108: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 109: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest - 15, // 110: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest - 11, // 111: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 20, // 112: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest - 23, // 113: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest - 26, // 114: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest - 29, // 115: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest - 32, // 116: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 117: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 118: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest - 36, // 119: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest - 39, // 120: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest - 41, // 121: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest - 48, // 122: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest - 49, // 123: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest - 50, // 124: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest - 53, // 125: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest - 58, // 126: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest - 60, // 127: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest - 61, // 128: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest - 64, // 129: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest - 66, // 130: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest - 67, // 131: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest - 68, // 132: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest - 70, // 133: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest - 72, // 134: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest - 75, // 135: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest - 77, // 136: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest - 78, // 137: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest - 82, // 138: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest - 84, // 139: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest - 86, // 140: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest - 87, // 141: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest - 89, // 142: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest - 91, // 143: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest - 92, // 144: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest - 99, // 145: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest - 101, // 146: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest - 102, // 147: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest - 104, // 148: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest - 105, // 149: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest - 106, // 150: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest - 107, // 151: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest - 108, // 152: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest - 109, // 153: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest - 120, // 154: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest - 125, // 155: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest - 126, // 156: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest - 123, // 157: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest - 127, // 158: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest - 121, // 159: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest - 122, // 160: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest - 124, // 161: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest - 128, // 162: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest - 115, // 163: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest - 132, // 164: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest - 133, // 165: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest - 134, // 166: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest - 135, // 167: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest - 137, // 168: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest - 139, // 169: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest - 140, // 170: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest - 143, // 171: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest - 144, // 172: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest - 111, // 173: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 174: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 175: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest - 114, // 176: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest - 145, // 177: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest - 146, // 178: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest - 148, // 179: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest - 149, // 180: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest - 153, // 181: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 0, // 182: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest - 157, // 183: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest - 153, // 184: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 158, // 185: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest - 159, // 186: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest - 58, // 187: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest - 160, // 188: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest - 162, // 189: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest - 163, // 190: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest - 166, // 191: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest - 165, // 192: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest - 164, // 193: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest - 169, // 194: gctrpc.GoCryptoTrader.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest - 1, // 195: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse - 7, // 196: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse - 131, // 197: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse - 131, // 198: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse - 10, // 199: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse - 4, // 200: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse - 13, // 201: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse - 131, // 202: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse - 19, // 203: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse - 14, // 204: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse - 16, // 205: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse - 131, // 206: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse - 22, // 207: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse - 25, // 208: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse - 28, // 209: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse - 31, // 210: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse - 35, // 211: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 212: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 213: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse - 37, // 214: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse - 40, // 215: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse - 47, // 216: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse - 131, // 217: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse - 131, // 218: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse - 52, // 219: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse - 55, // 220: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse - 59, // 221: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse - 56, // 222: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails - 63, // 223: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse - 65, // 224: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse - 65, // 225: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse - 131, // 226: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse - 69, // 227: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse - 71, // 228: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse - 74, // 229: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse - 76, // 230: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse - 131, // 231: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse - 81, // 232: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse - 83, // 233: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse - 85, // 234: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse - 88, // 235: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse - 88, // 236: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse - 90, // 237: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse - 93, // 238: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 93, // 239: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 100, // 240: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 100, // 241: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 103, // 242: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse - 131, // 243: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse - 28, // 244: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse - 28, // 245: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse - 22, // 246: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse - 22, // 247: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse - 110, // 248: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse - 131, // 249: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse - 131, // 250: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse - 130, // 251: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse - 129, // 252: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse - 130, // 253: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse - 131, // 254: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse - 131, // 255: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse - 129, // 256: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse - 131, // 257: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse - 116, // 258: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 131, // 259: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse - 131, // 260: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse - 131, // 261: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse - 136, // 262: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse - 138, // 263: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse - 131, // 264: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse - 142, // 265: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse - 131, // 266: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse - 131, // 267: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse - 113, // 268: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 269: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 270: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse - 116, // 271: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 147, // 272: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 147, // 273: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 131, // 274: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse - 152, // 275: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse - 154, // 276: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob - 156, // 277: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs - 156, // 278: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs - 154, // 279: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob - 131, // 280: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse - 131, // 281: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse - 59, // 282: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse - 161, // 283: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse - 167, // 284: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse - 131, // 285: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse - 131, // 286: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse - 131, // 287: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse - 131, // 288: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse - 170, // 289: gctrpc.GoCryptoTrader.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse - 195, // [195:290] is the sub-list for method output_type - 100, // [100:195] is the sub-list for method input_type - 100, // [100:100] is the sub-list for extension type_name - 100, // [100:100] is the sub-list for extension extendee - 0, // [0:100] is the sub-list for field type_name + 174, // 89: gctrpc.GetCollateralResponse.currencyBreakdown:type_name -> gctrpc.CollateralForCurrency + 9, // 90: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 3, // 91: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer + 9, // 92: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 18, // 93: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 44, // 94: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary + 45, // 95: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins + 46, // 96: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins + 186, // 97: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + 188, // 98: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + 80, // 99: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses + 18, // 100: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 0, // 101: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest + 6, // 102: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest + 5, // 103: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 5, // 104: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 8, // 105: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest + 2, // 106: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest + 12, // 107: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest + 11, // 108: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 109: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 110: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest + 15, // 111: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest + 11, // 112: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 20, // 113: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest + 23, // 114: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest + 26, // 115: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest + 29, // 116: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest + 32, // 117: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 118: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 119: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest + 36, // 120: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest + 39, // 121: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest + 41, // 122: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest + 48, // 123: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest + 49, // 124: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest + 50, // 125: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest + 53, // 126: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest + 58, // 127: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest + 60, // 128: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest + 61, // 129: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest + 64, // 130: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest + 66, // 131: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest + 67, // 132: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest + 68, // 133: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest + 70, // 134: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest + 72, // 135: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest + 75, // 136: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest + 77, // 137: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest + 78, // 138: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest + 82, // 139: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest + 84, // 140: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest + 86, // 141: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest + 87, // 142: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest + 89, // 143: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest + 91, // 144: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest + 92, // 145: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest + 99, // 146: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest + 101, // 147: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest + 102, // 148: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest + 104, // 149: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest + 105, // 150: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest + 106, // 151: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest + 107, // 152: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest + 108, // 153: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest + 109, // 154: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest + 120, // 155: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest + 125, // 156: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest + 126, // 157: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest + 123, // 158: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest + 127, // 159: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest + 121, // 160: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest + 122, // 161: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest + 124, // 162: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest + 128, // 163: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest + 115, // 164: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest + 132, // 165: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest + 133, // 166: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest + 134, // 167: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest + 135, // 168: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest + 137, // 169: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest + 139, // 170: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest + 140, // 171: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest + 143, // 172: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest + 144, // 173: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest + 111, // 174: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 175: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 176: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest + 114, // 177: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest + 145, // 178: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest + 146, // 179: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest + 148, // 180: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest + 149, // 181: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest + 153, // 182: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 0, // 183: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest + 157, // 184: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest + 153, // 185: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 158, // 186: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest + 159, // 187: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest + 58, // 188: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest + 160, // 189: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest + 162, // 190: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest + 163, // 191: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest + 166, // 192: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest + 165, // 193: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest + 164, // 194: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest + 169, // 195: gctrpc.GoCryptoTrader.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest + 172, // 196: gctrpc.GoCryptoTrader.GetCollateral:input_type -> gctrpc.GetCollateralRequest + 1, // 197: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse + 7, // 198: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse + 131, // 199: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse + 131, // 200: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse + 10, // 201: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse + 4, // 202: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse + 13, // 203: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse + 131, // 204: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse + 19, // 205: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse + 14, // 206: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse + 16, // 207: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse + 131, // 208: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse + 22, // 209: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse + 25, // 210: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse + 28, // 211: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse + 31, // 212: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse + 35, // 213: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 214: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 215: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse + 37, // 216: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse + 40, // 217: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse + 47, // 218: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse + 131, // 219: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse + 131, // 220: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse + 52, // 221: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse + 55, // 222: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse + 59, // 223: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse + 56, // 224: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails + 63, // 225: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse + 65, // 226: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse + 65, // 227: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse + 131, // 228: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse + 69, // 229: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse + 71, // 230: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse + 74, // 231: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse + 76, // 232: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse + 131, // 233: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse + 81, // 234: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse + 83, // 235: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse + 85, // 236: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse + 88, // 237: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse + 88, // 238: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse + 90, // 239: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse + 93, // 240: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 93, // 241: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 100, // 242: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 100, // 243: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 103, // 244: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse + 131, // 245: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse + 28, // 246: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse + 28, // 247: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse + 22, // 248: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse + 22, // 249: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse + 110, // 250: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse + 131, // 251: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse + 131, // 252: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse + 130, // 253: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse + 129, // 254: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse + 130, // 255: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse + 131, // 256: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse + 131, // 257: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse + 129, // 258: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse + 131, // 259: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse + 116, // 260: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 131, // 261: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse + 131, // 262: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse + 131, // 263: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse + 136, // 264: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse + 138, // 265: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse + 131, // 266: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse + 142, // 267: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse + 131, // 268: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse + 131, // 269: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse + 113, // 270: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 271: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 272: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse + 116, // 273: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 147, // 274: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 147, // 275: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 131, // 276: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse + 152, // 277: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse + 154, // 278: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob + 156, // 279: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs + 156, // 280: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs + 154, // 281: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob + 131, // 282: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse + 131, // 283: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse + 59, // 284: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse + 161, // 285: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse + 167, // 286: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse + 131, // 287: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse + 131, // 288: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse + 131, // 289: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse + 131, // 290: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse + 170, // 291: gctrpc.GoCryptoTrader.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse + 173, // 292: gctrpc.GoCryptoTrader.GetCollateral:output_type -> gctrpc.GetCollateralResponse + 197, // [197:293] is the sub-list for method output_type + 101, // [101:197] is the sub-list for method input_type + 101, // [101:101] is the sub-list for extension type_name + 101, // [101:101] is the sub-list for extension extendee + 0, // [0:101] is the sub-list for field type_name } func init() { file_rpc_proto_init() } @@ -15915,7 +16155,43 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[182].Exporter = func(v interface{}, i int) interface{} { + file_rpc_proto_msgTypes[172].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCollateralRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[173].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCollateralResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[174].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CollateralForCurrency); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[185].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelBatchOrdersResponse_Orders); i { case 0: return &v.state @@ -15927,7 +16203,7 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[184].Exporter = func(v interface{}, i int) interface{} { + file_rpc_proto_msgTypes[187].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelAllOrdersResponse_Orders); i { case 0: return &v.state @@ -15946,7 +16222,7 @@ func file_rpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rpc_proto_rawDesc, NumEnums: 0, - NumMessages: 188, + NumMessages: 191, NumExtensions: 0, NumServices: 1, }, diff --git a/gctrpc/rpc.pb.gw.go b/gctrpc/rpc.pb.gw.go index a1fee1e301d..763502513d5 100644 --- a/gctrpc/rpc.pb.gw.go +++ b/gctrpc/rpc.pb.gw.go @@ -3059,6 +3059,42 @@ func local_request_GoCryptoTrader_GetFuturesPositions_0(ctx context.Context, mar } +var ( + filter_GoCryptoTrader_GetCollateral_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_GetCollateral_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCollateralRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetCollateral_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetCollateral(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_GetCollateral_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetCollateralRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetCollateral_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetCollateral(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterGoCryptoTraderHandlerServer registers the http handlers for service GoCryptoTrader to "mux". // UnaryRPC :call GoCryptoTraderServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -5154,6 +5190,29 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetCollateral_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetCollateral", runtime.WithHTTPPathPattern("/v1/getcollateral")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_GetCollateral_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetCollateral_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7095,6 +7154,26 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetCollateral_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gctrpc.GoCryptoTrader/GetCollateral", runtime.WithHTTPPathPattern("/v1/getcollateral")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_GetCollateral_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetCollateral_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7288,6 +7367,8 @@ var ( pattern_GoCryptoTrader_CurrencyStateTradingPair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "currencystatetradingpair"}, "")) pattern_GoCryptoTrader_GetFuturesPositions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getfuturespositions"}, "")) + + pattern_GoCryptoTrader_GetCollateral_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getcollateral"}, "")) ) var ( @@ -7480,4 +7561,6 @@ var ( forward_GoCryptoTrader_CurrencyStateTradingPair_0 = runtime.ForwardResponseMessage forward_GoCryptoTrader_GetFuturesPositions_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_GetCollateral_0 = runtime.ForwardResponseMessage ) diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 1a5b7bea246..601cedad4bf 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1034,7 +1034,6 @@ message GetFuturesPositionsRequest { bool verbose = 8; } - message GetFuturesPositionsResponse { int64 totalOrders = 1; double totalRealisedPNL = 2; @@ -1053,6 +1052,26 @@ message FuturePosition { repeated OrderDetails orders = 7; } +message GetCollateralRequest { + string exchange =1; + string asset = 2; + string subAccount = 3; + bool includeBreakdown = 4; + bool calculateOffline = 5; +} + +message GetCollateralResponse { + string totalCollateral = 1; + repeated CollateralForCurrency currencyBreakdown = 2; +} + +message CollateralForCurrency { + string currency = 1; + string scaledCollateral = 2; + string scaledToCurrency = 3; +} + + service GoCryptoTrader { rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) { option (google.api.http) = { @@ -1647,4 +1666,9 @@ service GoCryptoTrader { get: "/v1/getfuturespositions" }; } + rpc GetCollateral (GetCollateralRequest) returns (GetCollateralResponse) { + option (google.api.http) = { + get: "/v1/getcollateral" + }; + } } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 378df8be90d..a336d1b6f03 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1159,6 +1159,60 @@ ] } }, + "/v1/getcollateral": { + "get": { + "operationId": "GoCryptoTrader_GetCollateral", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetCollateralResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "asset", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "subAccount", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "includeBreakdown", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "calculateOffline", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/getcommunicationrelayers": { "get": { "operationId": "GoCryptoTrader_GetCommunicationRelayers", @@ -3562,6 +3616,20 @@ } } }, + "gctrpcCollateralForCurrency": { + "type": "object", + "properties": { + "currency": { + "type": "string" + }, + "scaledCollateral": { + "type": "string" + }, + "scaledToCurrency": { + "type": "string" + } + } + }, "gctrpcCommunicationRelayer": { "type": "object", "properties": { @@ -4084,6 +4152,20 @@ } } }, + "gctrpcGetCollateralResponse": { + "type": "object", + "properties": { + "totalCollateral": { + "type": "string" + }, + "currencyBreakdown": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcCollateralForCurrency" + } + } + } + }, "gctrpcGetCommunicationRelayersResponse": { "type": "object", "properties": { diff --git a/gctrpc/rpc_grpc.pb.go b/gctrpc/rpc_grpc.pb.go index 3f7906ad55b..3bd6cc9061e 100644 --- a/gctrpc/rpc_grpc.pb.go +++ b/gctrpc/rpc_grpc.pb.go @@ -113,6 +113,7 @@ type GoCryptoTraderClient interface { CurrencyStateWithdraw(ctx context.Context, in *CurrencyStateWithdrawRequest, opts ...grpc.CallOption) (*GenericResponse, error) CurrencyStateTradingPair(ctx context.Context, in *CurrencyStateTradingPairRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetFuturesPositions(ctx context.Context, in *GetFuturesPositionsRequest, opts ...grpc.CallOption) (*GetFuturesPositionsResponse, error) + GetCollateral(ctx context.Context, in *GetCollateralRequest, opts ...grpc.CallOption) (*GetCollateralResponse, error) } type goCryptoTraderClient struct { @@ -1116,6 +1117,15 @@ func (c *goCryptoTraderClient) GetFuturesPositions(ctx context.Context, in *GetF return out, nil } +func (c *goCryptoTraderClient) GetCollateral(ctx context.Context, in *GetCollateralRequest, opts ...grpc.CallOption) (*GetCollateralResponse, error) { + out := new(GetCollateralResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GetCollateral", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GoCryptoTraderServer is the server API for GoCryptoTrader service. // All implementations must embed UnimplementedGoCryptoTraderServer // for forward compatibility @@ -1215,6 +1225,7 @@ type GoCryptoTraderServer interface { CurrencyStateWithdraw(context.Context, *CurrencyStateWithdrawRequest) (*GenericResponse, error) CurrencyStateTradingPair(context.Context, *CurrencyStateTradingPairRequest) (*GenericResponse, error) GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) + GetCollateral(context.Context, *GetCollateralRequest) (*GetCollateralResponse, error) mustEmbedUnimplementedGoCryptoTraderServer() } @@ -1507,6 +1518,9 @@ func (UnimplementedGoCryptoTraderServer) CurrencyStateTradingPair(context.Contex func (UnimplementedGoCryptoTraderServer) GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetFuturesPositions not implemented") } +func (UnimplementedGoCryptoTraderServer) GetCollateral(context.Context, *GetCollateralRequest) (*GetCollateralResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCollateral not implemented") +} func (UnimplementedGoCryptoTraderServer) mustEmbedUnimplementedGoCryptoTraderServer() {} // UnsafeGoCryptoTraderServer may be embedded to opt out of forward compatibility for this service. @@ -3248,6 +3262,24 @@ func _GoCryptoTrader_GetFuturesPositions_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _GoCryptoTrader_GetCollateral_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCollateralRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).GetCollateral(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/GetCollateral", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).GetCollateral(ctx, req.(*GetCollateralRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GoCryptoTrader_ServiceDesc is the grpc.ServiceDesc for GoCryptoTrader service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -3611,6 +3643,10 @@ var GoCryptoTrader_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetFuturesPositions", Handler: _GoCryptoTrader_GetFuturesPositions_Handler, }, + { + MethodName: "GetCollateral", + Handler: _GoCryptoTrader_GetCollateral_Handler, + }, }, Streams: []grpc.StreamDesc{ { From 7cd16ec98ea7ff0c7f4a7c43d415b93827fb6607 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 6 Jan 2022 11:25:22 +1100 Subject: [PATCH 053/171] Adds commands and testing for rpcserver.go functions --- cmd/gctcli/commands.go | 110 ++++++++++++++++++++++- cmd/gctcli/main.go | 1 + engine/rpcserver.go | 14 +-- engine/rpcserver_test.go | 187 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 301 insertions(+), 11 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 5a7f883b35d..eef898fad87 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4726,7 +4726,7 @@ func findMissingSavedCandleIntervals(c *cli.Context) error { var getFuturesPositionsCommand = &cli.Command{ Name: "getfuturesposition", Usage: "will retrieve all futures positions in a timeframe, then calculate PNL based on that. Note, the dates have an impact on PNL calculations, ensure your start date is not after a new position is opened", - ArgsUsage: " ", + ArgsUsage: " ", Action: getFuturesPositions, Flags: []cli.Flag{ &cli.StringFlag{ @@ -4905,3 +4905,111 @@ func getFuturesPositions(c *cli.Context) error { jsonOutput(result) return nil } + +var getCollateralCommand = &cli.Command{ + Name: "getcollateral", + Usage: "returns total collateral for an exchange asset, with optional per currency breakdown", + ArgsUsage: " ", + Action: getCollateral, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "exchange", + Aliases: []string{"e"}, + Usage: "the exchange to retrieve futures positions from", + }, + &cli.StringFlag{ + Name: "asset", + Aliases: []string{"a"}, + Usage: "the asset type of the currency pair, must be a futures type", + }, + &cli.BoolFlag{ + Name: "calculateoffline", + Aliases: []string{"c"}, + Usage: "use local scaling methods instead of requesting additional API information, depending on individual exchange support", + }, + &cli.BoolFlag{ + Name: "includebreakdown", + Aliases: []string{"i"}, + Usage: "include a list of each helds currency and its contribution to the overall collateral value", + }, + &cli.StringFlag{ + Name: "subaccount", + Aliases: []string{"s"}, + Usage: "the subaccount to retreieve collateral data from, depending on individual exchange support", + }, + }, +} + +func getCollateral(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, c.Command.Name) + } + + var exchangeName string + if c.IsSet("exchange") { + exchangeName = c.String("exchange") + } else { + exchangeName = c.Args().First() + } + + var assetType string + if c.IsSet("asset") { + assetType = c.String("asset") + } else { + assetType = c.Args().Get(1) + } + + if !validAsset(assetType) { + return errInvalidAsset + } + + var err error + var calculateOffline bool + if c.IsSet("calculateoffline") { + calculateOffline = c.Bool("calculateoffline") + } else if c.Args().Get(2) != "" { + calculateOffline, err = strconv.ParseBool(c.Args().Get(2)) + if err != nil { + return err + } + } + + var includeBreakdown bool + if c.IsSet("includebreakdown") { + includeBreakdown = c.Bool("includebreakdown") + } else if c.Args().Get(3) != "" { + includeBreakdown, err = strconv.ParseBool(c.Args().Get(3)) + if err != nil { + return err + } + } + + var subAccount string + if c.IsSet("subaccount") { + subAccount = c.String("subaccount") + } else if c.Args().Get(4) != "" { + subAccount = c.Args().Get(4) + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.GetCollateral(c.Context, + &gctrpc.GetCollateralRequest{ + Exchange: exchangeName, + Asset: assetType, + SubAccount: subAccount, + IncludeBreakdown: includeBreakdown, + CalculateOffline: calculateOffline, + }) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} diff --git a/cmd/gctcli/main.go b/cmd/gctcli/main.go index a6a09f9aaae..65ad498f86f 100644 --- a/cmd/gctcli/main.go +++ b/cmd/gctcli/main.go @@ -164,6 +164,7 @@ func main() { dataHistoryCommands, currencyStateManagementCommand, getFuturesPositionsCommand, + getCollateralCommand, } ctx, cancel := context.WithCancel(context.Background()) diff --git a/engine/rpcserver.go b/engine/rpcserver.go index a0b1cb4d593..f71747f2090 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4139,7 +4139,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture } } err = common.StartEndTimeCheck(start, end) - if err != nil { + if err != nil && !errors.Is(err, common.ErrDateUnset) { return nil, err } @@ -4227,7 +4227,9 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture return response, nil } -// GetCollateral returns pnl positions for an exchange asset pair +// GetCollateral returns the total collateral for an exchange's asset +// as exchanges can scale collateral and represent it in a singular currency, +// a user can opt to include a breakdown by currency func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRequest) (*gctrpc.GetCollateralResponse, error) { exch, err := s.GetExchangeByName(r.Exchange) if err != nil { @@ -4239,7 +4241,7 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if err != nil { return nil, err } - ai, err := exch.FetchAccountInfo(ctx.a) + ai, err := exch.FetchAccountInfo(ctx, a) if err != nil { return nil, err } @@ -4257,12 +4259,12 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe } else if len(ai.Accounts) > 0 { acc = ai.Accounts[0] } - for j := range acc.Currencies { + for i := range acc.Currencies { calculators = append(calculators, order.CollateralCalculator{ CalculateOffline: r.CalculateOffline, - CollateralCurrency: ai.Accounts[i].Currencies[j].CurrencyName, + CollateralCurrency: acc.Currencies[i].CurrencyName, Asset: a, - CollateralAmount: decimal.NewFromFloat(ai.Accounts[i].Currencies[j].TotalValue), + CollateralAmount: decimal.NewFromFloat(acc.Currencies[i].TotalValue), }) } diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 236f7f5a144..892d65cdaac 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" @@ -93,14 +94,61 @@ func (f fExchange) GetHistoricCandlesExtended(ctx context.Context, p currency.Pa // FetchAccountInfo overrides testExchange's fetch account info function // to do the bare minimum required with no API calls or credentials required -func (f fExchange) FetchAccountInfo(ctx context.Context, a asset.Item) (account.Holdings, error) { +func (f fExchange) FetchAccountInfo(_ context.Context, a asset.Item) (account.Holdings, error) { return account.Holdings{ Exchange: f.GetName(), Accounts: []account.SubAccount{ { - ID: "1337", - AssetType: a, - Currencies: nil, + ID: "1337", + AssetType: a, + Currencies: []account.Balance{ + { + CurrencyName: currency.USD, + TotalValue: 1337, + }, + }, + }, + }, + }, nil +} + +// GetFuturesPositions overrides testExchange's GetFuturesPositions function +func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp currency.Pair, _ time.Time, _ time.Time) ([]order.Detail, error) { + return []order.Detail{ + { + Price: 1337, + Amount: 1337, + Fee: 1.337, + FeeAsset: currency.Code{}, + Exchange: f.GetName(), + ID: "test", + Side: order.Long, + Status: order.Open, + AssetType: a, + Date: time.Now(), + Pair: cp, + }, + }, nil +} + +// CalculateTotalCollateral overrides testExchange's CalculateTotalCollateral function +func (f fExchange) CalculateTotalCollateral(context.Context, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { + return &order.TotalCollateralResponse{ + TotalCollateral: decimal.NewFromInt(1337), + BreakdownByCurrency: []order.CollateralByCurrency{ + { + Currency: currency.USD, + Amount: decimal.NewFromInt(1330), + }, + { + Currency: currency.DOGE, + Amount: decimal.NewFromInt(10), + ValueCurrency: currency.USD, + }, + { + Currency: currency.XRP, + Amount: decimal.NewFromInt(-3), + ValueCurrency: currency.USD, }, }, }, nil @@ -1977,3 +2025,134 @@ func TestCurrencyStateTradingPair(t *testing.T) { t.Fatalf("received: %v, but expected: %v", err, nil) } } + +func TestGetFuturesPositions(t *testing.T) { + t.Parallel() + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + b := exch.GetBase() + b.Name = fakeExchangeName + b.Enabled = true + + cp, err := currency.NewPairFromString("btc-usd") + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } + fakeExchange := fExchange{ + IBotExchange: exch, + } + em.Add(fakeExchange) + var wg sync.WaitGroup + om, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + om.started = 1 + s := RPCServer{ + Engine: &Engine{ + ExchangeManager: em, + currencyStateManager: &CurrencyStateManager{ + started: 1, iExchangeManager: em, + }, + OrderManager: om, + }, + } + + r, err := s.GetFuturesPositions(context.Background(), &gctrpc.GetFuturesPositionsRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + Pair: &gctrpc.CurrencyPair{ + Delimiter: currency.DashDelimiter, + Base: cp.Base.String(), + Quote: cp.Quote.String(), + }, + Verbose: true, + }) + if err != nil { + t.Error(err) + } + if r == nil { + t.Fatal("expected not nil response") + } + if len(r.Positions) != 1 { + t.Fatal("expected 1 position") + } + if r.TotalOrders != 1 { + t.Fatal("expected 1 order") + } +} + +func TestGetCollateral(t *testing.T) { + t.Parallel() + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + b := exch.GetBase() + b.Name = fakeExchangeName + b.Enabled = true + + cp, err := currency.NewPairFromString("btc-usd") + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } + fakeExchange := fExchange{ + IBotExchange: exch, + } + em.Add(fakeExchange) + s := RPCServer{ + Engine: &Engine{ + ExchangeManager: em, + currencyStateManager: &CurrencyStateManager{ + started: 1, iExchangeManager: em, + }, + }, + } + + r, err := s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + }) + if err != nil { + t.Error(err) + } + if len(r.CurrencyBreakdown) > 0 { + t.Error("expected no breakdown") + } + + r, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + IncludeBreakdown: true, + SubAccount: "1337", + }) + if err != nil { + t.Error(err) + } + if len(r.CurrencyBreakdown) != 3 { + t.Error("expected 3 currencies") + } + if r.TotalCollateral != "1337" { + t.Error("expected 1337") + } +} From 51e87133eedfb518bc7dc74b439362ce57f94e86 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 6 Jan 2022 16:12:00 +1100 Subject: [PATCH 054/171] Increase testing and fix up backtester code --- backtester/backtest/backtest.go | 20 +- backtester/common/common_types.go | 6 - ...a-api-candles-exchange-level-funding.strat | 4 +- .../dca-api-candles-multiple-currencies.strat | 4 +- ...-api-candles-simultaneous-processing.strat | 4 +- .../config/examples/dca-api-candles.strat | 4 +- .../config/examples/dca-api-trades.strat | 4 +- .../examples/dca-database-candles.strat | 4 +- .../config/examples/rsi-api-candles.strat | 4 +- .../t2b2-api-candles-exchange-funding.strat | 4 +- backtester/data/kline/kline_types.go | 2 - .../eventhandlers/portfolio/portfolio.go | 118 +++++------- .../eventhandlers/portfolio/portfolio_test.go | 67 ++++--- .../portfolio/portfolio_types.go | 4 +- backtester/eventtypes/kline/kline.go | 18 -- backtester/eventtypes/kline/kline_types.go | 9 - engine/order_manager.go | 41 ++-- engine/order_manager_test.go | 180 ++++++++++++++++++ engine/order_manager_types.go | 12 +- exchanges/ftx/ftx_test.go | 2 +- exchanges/order/futures.go | 14 +- exchanges/order/futures_test.go | 16 +- exchanges/order/futures_types.go | 9 +- 23 files changed, 352 insertions(+), 198 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index ed95f176272..e1dff5daada 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -49,6 +49,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) +var futuresEnabled = false + // New returns a new BackTest instance func New() *BackTest { return &BackTest{ @@ -163,8 +165,6 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, return nil, err } conf.Enabled = true - // because for some reason we're supposed to set this - // even if no websocket is enabled err = exch.Setup(conf) if err != nil { return nil, err @@ -177,6 +177,10 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, } assets := exchBase.CurrencyPairs.GetAssetTypes(false) for i := range assets { + if assets[i] != asset.Spot { + // only spot is supported at this time + continue + } exchBase.CurrencyPairs.Pairs[assets[i]].AssetEnabled = convert.BoolPtr(true) err = exch.SetPairs(exchBase.CurrencyPairs.Pairs[assets[i]].Available, assets[i], true) if err != nil { @@ -264,8 +268,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, var baseItem, quoteItem, futureItem *funding.Item if cfg.StrategySettings.UseExchangeLevelFunding { - switch a { - case asset.Spot: + switch { + case a == asset.Spot: // add any remaining currency items that have no funding data in the strategy config baseItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, a, @@ -293,7 +297,10 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { return nil, err } - case asset.Futures, asset.USDTMarginedFutures, asset.CoinMarginedFutures: + case a.IsFutures(): + if !futuresEnabled { + return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) + } // setup contract items c := funding.CreateFuturesCurrencyCode(b, q) futureItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, @@ -316,9 +323,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, if err != nil { return nil, err } - default: - return nil, fmt.Errorf("%w %v unsupported", errInvalidConfigAsset, a) + return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) } } else { var bFunds, qFunds decimal.Decimal diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 72e6f95ef6c..75071005c13 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -75,12 +75,6 @@ type DataEventHandler interface { GetHighPrice() decimal.Decimal GetLowPrice() decimal.Decimal GetOpenPrice() decimal.Decimal - GetFuturesDataEventHandler() (FuturesDataEventHandler, error) -} - -type FuturesDataEventHandler interface { - GetMarkPrice() decimal.Decimal - GetPreviousMarkPrice() decimal.Decimal } // Directioner dictates the side of an order diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index e2f3112b0ff..6f0391ec142 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -71,8 +71,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/dca-api-candles-multiple-currencies.strat b/backtester/config/examples/dca-api-candles-multiple-currencies.strat index 17ba2aa1ef6..9f15b4cb3a6 100644 --- a/backtester/config/examples/dca-api-candles-multiple-currencies.strat +++ b/backtester/config/examples/dca-api-candles-multiple-currencies.strat @@ -67,8 +67,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat index fd2229b8b01..f9588431c9c 100644 --- a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat +++ b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat @@ -67,8 +67,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/dca-api-candles.strat b/backtester/config/examples/dca-api-candles.strat index 56618d23235..0ae4872666a 100644 --- a/backtester/config/examples/dca-api-candles.strat +++ b/backtester/config/examples/dca-api-candles.strat @@ -40,8 +40,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/dca-api-trades.strat b/backtester/config/examples/dca-api-trades.strat index 9606f67af81..c6e8ac823ff 100644 --- a/backtester/config/examples/dca-api-trades.strat +++ b/backtester/config/examples/dca-api-trades.strat @@ -40,8 +40,8 @@ "interval": 3600000000000, "data-type": "trade", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-08-04T00:00:00+10:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-08-04T00:00:00+10:00", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/dca-database-candles.strat b/backtester/config/examples/dca-database-candles.strat index d5300fc4f97..9921eac777d 100644 --- a/backtester/config/examples/dca-database-candles.strat +++ b/backtester/config/examples/dca-database-candles.strat @@ -40,8 +40,8 @@ "interval": 86400000000000, "data-type": "candle", "database-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "config": { "enabled": true, "verbose": false, diff --git a/backtester/config/examples/rsi-api-candles.strat b/backtester/config/examples/rsi-api-candles.strat index 7fce38b2696..60978657860 100644 --- a/backtester/config/examples/rsi-api-candles.strat +++ b/backtester/config/examples/rsi-api-candles.strat @@ -73,8 +73,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "inclusive-end-date": false } }, diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 8ac70c69026..662e6853f6b 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -180,8 +180,8 @@ "interval": 86400000000000, "data-type": "candle", "api-data": { - "start-date": "2020-08-01T00:00:00+10:00", - "end-date": "2020-12-01T00:00:00+11:00", + "start-date": "2021-08-01T00:00:00+10:00", + "end-date": "2021-12-01T00:00:00+11:00", "inclusive-end-date": false } }, diff --git a/backtester/data/kline/kline_types.go b/backtester/data/kline/kline_types.go index 46fc596a7e8..d44d395f51c 100644 --- a/backtester/data/kline/kline_types.go +++ b/backtester/data/kline/kline_types.go @@ -5,7 +5,6 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/backtester/data" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) @@ -17,6 +16,5 @@ type DataFromKline struct { data.Base addedTimes map[time.Time]bool Item gctkline.Item - FuturesData []kline.FuturesData RangeHolder *gctkline.IntervalRangeHolder } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 42c7cb3a4de..f8b3e96acee 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -1,6 +1,7 @@ package portfolio import ( + "context" "errors" "fmt" "time" @@ -16,7 +17,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" - gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -48,6 +48,47 @@ func (p *Portfolio) Reset() { p.exchangeAssetPairSettings = nil } +// SetupCurrencySettingsMap ensures a map is created and no panics happen +func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch gctexchange.IBotExchange) error { + if settings == nil { + return errNoPortfolioSettings + } + if settings.Exchange == "" { + return errExchangeUnset + } + if settings.Asset == "" { + return errAssetUnset + } + if settings.Pair.IsEmpty() { + return errCurrencyPairUnset + } + if p.exchangeAssetPairSettings == nil { + p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) + } + if p.exchangeAssetPairSettings[settings.Exchange] == nil { + p.exchangeAssetPairSettings[settings.Exchange] = make(map[asset.Item]map[currency.Pair]*Settings) + } + if p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] == nil { + p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] = make(map[currency.Pair]*Settings) + } + if _, ok := p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair]; ok { + return nil + } + + p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair] = &Settings{ + Fee: settings.ExchangeFee, + BuySideSizing: settings.BuySide, + SellSideSizing: settings.SellSide, + Leverage: settings.Leverage, + ComplianceManager: compliance.Manager{ + Snapshots: []compliance.Snapshot{}, + }, + Exchange: exch, + FuturesTracker: gctorder.SetupPositionController(), + } + return nil +} + // OnSignal receives the event from the strategy on whether it has signalled to buy, do nothing or sell // on buy/sell, the portfolio manager will size the order and assess the risk of the order // if successful, it will pass on an order.Order to be used by the exchange event handler to place an order based on @@ -136,17 +177,15 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return nil, err } sizingFunds = cReader.AvailableFunds() - sizingFunds, err = lookup.Exchange.ScaleCollateral(&gctorder.CollateralCalculator{ + sizingFunds, err = lookup.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ CollateralCurrency: cReader.CollateralCurrency(), Asset: ev.GetAssetType(), Side: ev.GetDirection(), CollateralAmount: sizingFunds, USDPrice: ev.GetPrice(), + CalculateOffline: true, }) if err != nil { - if errors.Is(err, gctcommon.ErrFunctionNotSupported) { - - } return nil, err } @@ -469,64 +508,6 @@ func (p *Portfolio) ViewHoldingAtTimePeriod(ev common.EventHandler) (*holdings.H return nil, fmt.Errorf("%w for %v %v %v at %v", errNoHoldings, ev.GetExchange(), ev.GetAssetType(), ev.Pair(), ev.GetTime()) } -// SetupCurrencySettingsMap ensures a map is created and no panics happen -func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch gctexchange.IBotExchange) error { - if settings == nil { - return errNoPortfolioSettings - } - if settings.Exchange == "" { - return errExchangeUnset - } - if settings.Asset == "" { - return errAssetUnset - } - if settings.Pair.IsEmpty() { - return errCurrencyPairUnset - } - if p.exchangeAssetPairSettings == nil { - p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) - } - if p.exchangeAssetPairSettings[settings.Exchange] == nil { - p.exchangeAssetPairSettings[settings.Exchange] = make(map[asset.Item]map[currency.Pair]*Settings) - } - if p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] == nil { - p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] = make(map[currency.Pair]*Settings) - } - if _, ok := p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair]; ok { - return nil - } - - var pnl gctorder.PNLCalculation - if settings.UseExchangePNLCalculation { - pnl = exch - } - ptc, err := gctorder.SetupMultiPositionTracker(&gctorder.PositionControllerSetup{ - Exchange: exch.GetName(), - Asset: settings.Asset, - Pair: settings.Pair, - Underlying: settings.Pair.Base, - OfflineCalculation: !settings.UseRealOrders, - UseExchangePNLCalculation: settings.UseExchangePNLCalculation, - ExchangePNLCalculation: pnl, - }) - if err != nil { - return fmt.Errorf("unable to setup currency settings %w", err) - } - - p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair] = &Settings{ - Fee: settings.ExchangeFee, - BuySideSizing: settings.BuySide, - SellSideSizing: settings.SellSide, - Leverage: settings.Leverage, - ComplianceManager: compliance.Manager{ - Snapshots: []compliance.Snapshot{}, - }, - Exchange: exch, - FuturesTracker: ptc, - } - return nil -} - // GetLatestHoldings returns the latest holdings after being sorted by time func (e *Settings) GetLatestHoldings() holdings.Holding { if len(e.HoldingsSnapshots) == 0 { @@ -594,9 +575,9 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er if settings.FuturesTracker == nil { return nil, errors.New("no futures tracker") } - positions := settings.FuturesTracker.GetPositions() - if positions == nil { - return nil, nil + positions, err := settings.FuturesTracker.GetPositionsForExchange(e.GetExchange(), e.GetAssetType(), e.Pair()) + if err != nil { + return nil, err } pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() if err != nil { @@ -626,7 +607,10 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { if settings.FuturesTracker == nil { continue } - positions := settings.FuturesTracker.GetPositions() + positions, err := settings.FuturesTracker.GetPositionsForExchange(exchK, assetK, pairK) + if err != nil { + return nil + } pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() if err != nil { continue diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 95394648e12..5a5efaae98a 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -254,11 +254,11 @@ func TestUpdate(t *testing.T) { if !errors.Is(err, funding.ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, funding.ErrFundsNotFound) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) if err != nil { t.Fatal(err) } @@ -406,11 +406,11 @@ func TestOnFill(t *testing.T) { t.Error(err) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) if err != nil { t.Fatal(err) } @@ -456,11 +456,11 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, funding.ErrFundsNotFound) { t.Errorf("received: %v, expected: %v", err, funding.ErrFundsNotFound) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero, false) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero, false) if err != nil { t.Fatal(err) } @@ -581,16 +581,18 @@ func TestGetSnapshotAtTime(t *testing.T) { if !ok { t.Fatal("couldn't get settings") } - err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ - { - Order: &gctorder.Detail{ - Exchange: "exch", - AssetType: asset.Spot, - Pair: cp, - Amount: 1337, + err = s.ComplianceManager.AddSnapshot(&compliance.Snapshot{ + Orders: []compliance.SnapshotOrder{ + { + Order: &gctorder.Detail{ + Exchange: "exch", + AssetType: asset.Spot, + Pair: cp, + Amount: 1337, + }, }, }, - }, tt, 0, false) + }, true) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -633,16 +635,18 @@ func TestGetLatestSnapshot(t *testing.T) { if !ok { t.Fatal("couldn't get settings") } - err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ - { - Order: &gctorder.Detail{ - Exchange: "exch", - AssetType: asset.Spot, - Pair: cp, - Amount: 1337, + err = s.ComplianceManager.AddSnapshot(&compliance.Snapshot{ + Orders: []compliance.SnapshotOrder{ + { + Order: &gctorder.Detail{ + Exchange: "exch", + AssetType: asset.Spot, + Pair: cp, + Amount: 1337, + }, }, }, - }, tt, 0, false) + }, true) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -651,17 +655,18 @@ func TestGetLatestSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, nil) } - err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ - ss[0].Orders[0], - { - Order: &gctorder.Detail{ - Exchange: "exch", - AssetType: asset.Spot, - Pair: cp, - Amount: 1338, + err = s.ComplianceManager.AddSnapshot(&compliance.Snapshot{ + Orders: []compliance.SnapshotOrder{ + { + Order: &gctorder.Detail{ + Exchange: "exch", + AssetType: asset.Spot, + Pair: cp, + Amount: 1337, + }, }, }, - }, tt, 1, false) + }, true) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 4f4a7419331..d20da2520fc 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -83,10 +83,10 @@ type Settings struct { HoldingsSnapshots []holdings.Holding ComplianceManager compliance.Manager Exchange gctexchange.IBotExchange - FuturesTracker *gctorder.MultiPositionTracker + FuturesTracker *gctorder.PositionController } -// PNLSummary holds a PNL result along with +// PNLSummary holds a PNL result along with // exchange details type PNLSummary struct { Exchange string diff --git a/backtester/eventtypes/kline/kline.go b/backtester/eventtypes/kline/kline.go index 0a689ba3d3a..82efa97b9dd 100644 --- a/backtester/eventtypes/kline/kline.go +++ b/backtester/eventtypes/kline/kline.go @@ -1,10 +1,7 @@ package kline import ( - "fmt" - "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" ) // GetClosePrice returns the closing price of a kline @@ -26,18 +23,3 @@ func (k *Kline) GetLowPrice() decimal.Decimal { func (k *Kline) GetOpenPrice() decimal.Decimal { return k.Open } - -func (k *Kline) GetFuturesDataEventHandler() (common.FuturesDataEventHandler, error) { - if !k.AssetType.IsFutures() { - return nil, fmt.Errorf("not futures") - } - return k.FuturesData, nil -} - -func (f *FuturesData) GetMarkPrice() decimal.Decimal { - return f.MarkPrice -} - -func (f *FuturesData) GetPreviousMarkPrice() decimal.Decimal { - return f.PrevMarkPrice -} diff --git a/backtester/eventtypes/kline/kline_types.go b/backtester/eventtypes/kline/kline_types.go index 6e4a5e5ecbd..71121e5b080 100644 --- a/backtester/eventtypes/kline/kline_types.go +++ b/backtester/eventtypes/kline/kline_types.go @@ -1,8 +1,6 @@ package kline import ( - "time" - "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" ) @@ -17,11 +15,4 @@ type Kline struct { High decimal.Decimal Volume decimal.Decimal ValidationIssues string - FuturesData *FuturesData -} - -type FuturesData struct { - Time time.Time - MarkPrice decimal.Decimal - PrevMarkPrice decimal.Decimal } diff --git a/engine/order_manager.go b/engine/order_manager.go index 52f97b9bf4b..9a400d0eedc 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -34,11 +34,11 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i return &OrderManager{ shutdown: make(chan struct{}), orderStore: store{ - Orders: make(map[string][]*order.Detail), - exchangeManager: exchangeManager, - commsManager: communicationsManager, - wg: wg, - futuresPositionTrackers: order.SetupPositionController(), + Orders: make(map[string][]*order.Detail), + exchangeManager: exchangeManager, + commsManager: communicationsManager, + wg: wg, + futuresPositionController: order.SetupPositionController(), }, verbose: verbose, }, nil @@ -225,6 +225,8 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { return nil } +var errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") + // GetFuturesPositionsForExchange returns futures positions stored within // the order manager's futures position tracker that match the provided params func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*order.PositionTracker, error) { @@ -234,8 +236,14 @@ func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.It if atomic.LoadInt32(&m.started) == 0 { return nil, fmt.Errorf("order manager %w", ErrSubSystemNotStarted) } + if m.orderStore.futuresPositionController == nil { + return nil, errFuturesTrackerNotSetup + } + if !item.IsFutures() { + return nil, fmt.Errorf("%v %w", item, order.ErrNotFutureAsset) + } - return m.orderStore.futuresPositionTrackers.GetPositionsForExchange(exch, item, pair) + return m.orderStore.futuresPositionController.GetPositionsForExchange(exch, item, pair) } // GetOrderInfo calls the exchange's wrapper GetOrderInfo function @@ -272,11 +280,11 @@ func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID s // validate ensures a submitted order is valid before adding to the manager func (m *OrderManager) validate(newOrder *order.Submit) error { if newOrder == nil { - return errors.New("order cannot be nil") + return errNilOrder } if newOrder.Exchange == "" { - return errors.New("order exchange name must be specified") + return errExchangeNameIsEmpty } if err := newOrder.Validate(); err != nil { @@ -511,10 +519,12 @@ func (m *OrderManager) GetOrdersActive(f *order.Filter) ([]order.Detail, error) return m.orderStore.getActiveOrders(f), nil } +var errUnableToPlaceOrder = errors.New("cannot process order, order not placed") + // processSubmittedOrder adds a new order to the manager func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result order.SubmitResponse) (*OrderSubmitResponse, error) { if !result.IsOrderPlaced { - return nil, errors.New("order unable to be placed") + return nil, errUnableToPlaceOrder } id, err := uuid.NewV4() @@ -840,6 +850,9 @@ func (s *store) getByExchangeAndID(exchange, id string) (*order.Detail, error) { // updateExisting checks if an order exists in the orderstore // and then updates it func (s *store) updateExisting(od *order.Detail) error { + if od == nil { + return errNilOrder + } s.m.Lock() defer s.m.Unlock() r, ok := s.Orders[strings.ToLower(od.Exchange)] @@ -850,7 +863,7 @@ func (s *store) updateExisting(od *order.Detail) error { if r[x].ID == od.ID { r[x].UpdateOrderFromDetail(od) if r[x].AssetType.IsFutures() { - err := s.futuresPositionTrackers.TrackNewOrder(r[x]) + err := s.futuresPositionController.TrackNewOrder(r[x]) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { return err @@ -877,7 +890,7 @@ func (s *store) modifyExisting(id string, mod *order.Modify) error { if r[x].ID == id { r[x].UpdateOrderFromModify(mod) if r[x].AssetType.IsFutures() { - err := s.futuresPositionTrackers.TrackNewOrder(r[x]) + err := s.futuresPositionController.TrackNewOrder(r[x]) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { return err @@ -904,7 +917,7 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) s.m.Lock() defer s.m.Unlock() if od.AssetType.IsFutures() { - err = s.futuresPositionTrackers.TrackNewOrder(od) + err = s.futuresPositionController.TrackNewOrder(od) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { return nil, err @@ -990,7 +1003,7 @@ func (s *store) exists(det *order.Detail) bool { // Add Adds an order to the orderStore for tracking the lifecycle func (s *store) add(det *order.Detail) error { if det == nil { - return errors.New("order store: Order is nil") + return errNilOrder } _, err := s.exchangeManager.GetExchangeByName(det.Exchange) if err != nil { @@ -1008,7 +1021,7 @@ func (s *store) add(det *order.Detail) error { s.Orders[strings.ToLower(det.Exchange)] = orders if det.AssetType.IsFutures() { - err = s.futuresPositionTrackers.TrackNewOrder(det) + err = s.futuresPositionController.TrackNewOrder(det) if err != nil { return err } diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 95bed7637c7..9816ce00031 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -3,6 +3,7 @@ package engine import ( "context" "errors" + "strings" "sync" "testing" "time" @@ -1152,3 +1153,182 @@ func Test_getActiveOrders(t *testing.T) { t.Errorf("Test_getActiveOrders - Expected 0 results, got: %d", len(res)) } } + +func TestGetFuturesPositionsForExchange(t *testing.T) { + t.Parallel() + o := &OrderManager{} + cp := currency.NewPair(currency.BTC, currency.USDT) + _, err := o.GetFuturesPositionsForExchange("test", asset.Spot, cp) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + _, err = o.GetFuturesPositionsForExchange("test", asset.Spot, cp) + if !errors.Is(err, errFuturesTrackerNotSetup) { + t.Errorf("received '%v', expected '%v'", err, errFuturesTrackerNotSetup) + } + o.orderStore.futuresPositionController = order.SetupPositionController() + _, err = o.GetFuturesPositionsForExchange("test", asset.Spot, cp) + if !errors.Is(err, order.ErrNotFutureAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFutureAsset) + } + + _, err = o.GetFuturesPositionsForExchange("test", asset.Futures, cp) + if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) + } + o = nil + _, err = o.GetFuturesPositionsForExchange("test", asset.Futures, cp) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } +} + +func TestSubmitFakeOrder(t *testing.T) { + t.Parallel() + o := &OrderManager{} + resp := order.SubmitResponse{} + _, err := o.SubmitFakeOrder(nil, resp, false) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + + o.started = 1 + _, err = o.SubmitFakeOrder(nil, resp, false) + if !errors.Is(err, errNilOrder) { + t.Errorf("received '%v', expected '%v'", err, errNilOrder) + } + ord := &order.Submit{} + _, err = o.SubmitFakeOrder(ord, resp, false) + if !errors.Is(err, errExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", err, errExchangeNameIsEmpty) + } + ord.Exchange = testExchange + ord.AssetType = asset.Spot + ord.Pair = currency.NewPair(currency.BTC, currency.DOGE) + ord.Side = order.Buy + ord.Type = order.Market + ord.Amount = 1337 + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + exch.SetDefaults() + em.Add(exch) + o.orderStore.exchangeManager = em + + _, err = o.SubmitFakeOrder(ord, resp, false) + if !errors.Is(err, errUnableToPlaceOrder) { + t.Errorf("received '%v', expected '%v'", err, errUnableToPlaceOrder) + } + + resp.IsOrderPlaced = true + resp.FullyMatched = true + o.orderStore.commsManager = &CommunicationManager{} + o.orderStore.Orders = make(map[string][]*order.Detail) + _, err = o.SubmitFakeOrder(ord, resp, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } +} + +func TestGetOrdersSnapshot(t *testing.T) { + t.Parallel() + o := &OrderManager{} + o.GetOrdersSnapshot(order.AnyStatus) + o.started = 1 + o.orderStore.Orders = make(map[string][]*order.Detail) + o.orderStore.Orders[testExchange] = []*order.Detail{ + { + Status: order.Open, + }, + } + snap := o.GetOrdersSnapshot(order.Open) + if len(snap) != 1 { + t.Error("expected 1") + } + snap = o.GetOrdersSnapshot(order.Closed) + if len(snap) != 0 { + t.Error("expected 0") + } +} + +func TestUpdateExisting(t *testing.T) { + t.Parallel() + s := &store{} + s.Orders = make(map[string][]*order.Detail) + err := s.updateExisting(nil) + if !errors.Is(err, errNilOrder) { + t.Errorf("received '%v', expected '%v'", err, errNilOrder) + } + od := &order.Detail{Exchange: testExchange} + err = s.updateExisting(od) + if !errors.Is(err, ErrExchangeNotFound) { + t.Errorf("received '%v', expected '%v'", err, ErrExchangeNotFound) + } + s.Orders[strings.ToLower(testExchange)] = nil + err = s.updateExisting(od) + if !errors.Is(err, ErrOrderNotFound) { + t.Errorf("received '%v', expected '%v'", err, ErrOrderNotFound) + } + od.AssetType = asset.Futures + od.ID = "123" + od.Pair = currency.NewPair(currency.BTC, currency.USDT) + od.Side = order.Buy + od.Type = order.Market + od.Date = time.Now() + od.Amount = 1337 + s.Orders[strings.ToLower(testExchange)] = []*order.Detail{ + od, + } + s.futuresPositionController = order.SetupPositionController() + err = s.updateExisting(od) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + pos, err := s.futuresPositionController.GetPositionsForExchange(testExchange, asset.Futures, od.Pair) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + if len(pos) != 1 { + t.Error("expected 1") + } +} + +func TestOrderManagerExists(t *testing.T) { + t.Parallel() + o := &OrderManager{} + if o.Exists(nil) { + t.Error("expected false") + } + o.started = 1 + if o.Exists(nil) { + t.Error("expected false") + } + + o = nil + if o.Exists(nil) { + t.Error("expected false") + } +} + +func TestOrderManagerAdd(t *testing.T) { + t.Parallel() + o := &OrderManager{} + err := o.Add(nil) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + err = o.Add(nil) + if !errors.Is(err, errNilOrder) { + t.Errorf("received '%v', expected '%v'", err, errNilOrder) + } + + o = nil + err = o.Add(nil) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } +} diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index 58724805a51..50dcbae5e27 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -37,12 +37,12 @@ type orderManagerConfig struct { // store holds all orders by exchange type store struct { - m sync.RWMutex - Orders map[string][]*order.Detail - commsManager iCommsManager - exchangeManager iExchangeManager - wg *sync.WaitGroup - futuresPositionTrackers *order.PositionController + m sync.RWMutex + Orders map[string][]*order.Detail + commsManager iCommsManager + exchangeManager iExchangeManager + wg *sync.WaitGroup + futuresPositionController *order.PositionController } // OrderManager processes and stores orders across enabled exchanges diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 27f47100d12..323782cfcc5 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1890,7 +1890,7 @@ func TestCalculatePNL(t *testing.T) { exch := f.Name item := asset.Futures - setup := &order.PositionControllerSetup{ + setup := &order.MultiPositionTrackerSetup{ Exchange: exch, Asset: item, Pair: pair, diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index f5fbb861eda..7a9488c4948 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -28,7 +28,7 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return errNilOrder } if !d.AssetType.IsFutures() { - return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, errNotFutureAsset) + return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, ErrNotFutureAsset) } if c == nil { return common.ErrNilPointer @@ -44,7 +44,7 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { var err error mpt, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)][d.AssetType][d.Pair] if !ok { - mpt, err = SetupMultiPositionTracker(&PositionControllerSetup{ + mpt, err = SetupMultiPositionTracker(&MultiPositionTrackerSetup{ Exchange: strings.ToLower(d.Exchange), Asset: d.AssetType, Pair: d.Pair, @@ -67,9 +67,9 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite c.m.Lock() defer c.m.Unlock() if !item.IsFutures() { - return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, errNotFutureAsset) + return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFutureAsset) } - exchM, ok := c.positionTrackerControllers[exch] + exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] if !ok { return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) } @@ -85,7 +85,7 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite } // SetupMultiPositionTracker creates a futures order tracker for a specific exchange -func SetupMultiPositionTracker(setup *PositionControllerSetup) (*MultiPositionTracker, error) { +func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPositionTracker, error) { if setup == nil { return nil, errNilSetup } @@ -93,7 +93,7 @@ func SetupMultiPositionTracker(setup *PositionControllerSetup) (*MultiPositionTr return nil, errExchangeNameEmpty } if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { - return nil, errNotFutureAsset + return nil, ErrNotFutureAsset } if setup.Pair.IsEmpty() { return nil, ErrPairIsEmpty @@ -194,7 +194,7 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) return nil, errNilSetup } if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { - return nil, errNotFutureAsset + return nil, ErrNotFutureAsset } if setup.Pair.IsEmpty() { return nil, ErrPairIsEmpty diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 6e3b02c8728..4bc0d815ac4 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -185,14 +185,14 @@ func TestSetupMultiPositionTracker(t *testing.T) { t.Error(err) } - setup := &PositionControllerSetup{} + setup := &MultiPositionTrackerSetup{} _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, errExchangeNameEmpty) { t.Error(err) } setup.Exchange = "test" _, err = SetupMultiPositionTracker(setup) - if !errors.Is(err, errNotFutureAsset) { + if !errors.Is(err, ErrNotFutureAsset) { t.Error(err) } setup.Asset = asset.Futures @@ -234,7 +234,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { exch := "test" item := asset.Futures pair := currency.NewPair(currency.BTC, currency.USDT) - setup := &PositionControllerSetup{ + setup := &MultiPositionTrackerSetup{ Exchange: exch, Asset: item, Pair: pair, @@ -352,7 +352,7 @@ func TestPositionControllerTestTrackNewOrder(t *testing.T) { Side: Long, ID: "lol", }) - if !errors.Is(err, errNotFutureAsset) { + if !errors.Is(err, ErrNotFutureAsset) { t.Error(err) } @@ -475,8 +475,8 @@ func TestGetPositionsForExchange(t *testing.T) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) } pos, err = c.GetPositionsForExchange("test", asset.Spot, p) - if !errors.Is(err, errNotFutureAsset) { - t.Errorf("received '%v' expected '%v", err, errNotFutureAsset) + if !errors.Is(err, ErrNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) } c.positionTrackerControllers["test"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) @@ -568,8 +568,8 @@ func TestSetupPositionTracker(t *testing.T) { p, err = m.SetupPositionTracker(&PositionTrackerSetup{ Asset: asset.Spot, }) - if !errors.Is(err, errNotFutureAsset) { - t.Errorf("received '%v' expected '%v", err, errNotFutureAsset) + if !errors.Is(err, ErrNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) } if p != nil { t.Error("expected nil") diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 0a0ae5454dd..4052dca6576 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -26,9 +26,10 @@ var ( // ErrPositionLiquidated is raised when checking PNL status only for // it to be liquidated ErrPositionLiquidated = errors.New("position liquidated") + // ErrNotFutureAsset returned when futures data is requested on a non-futures asset + ErrNotFutureAsset = errors.New("asset type is not futures") errExchangeNameEmpty = errors.New("exchange name empty") - errNotFutureAsset = errors.New("asset type is not futures") errTimeUnset = errors.New("time unset") errMissingPNLCalculationFunctions = errors.New("futures tracker requires exchange PNL calculation functions") errOrderNotEqualToTracker = errors.New("order does not match tracker data") @@ -101,9 +102,9 @@ type MultiPositionTracker struct { exchangePNLCalculation PNLCalculation } -// PositionControllerSetup holds the parameters -// required to set up a position controller -type PositionControllerSetup struct { +// MultiPositionTrackerSetup holds the parameters +// required to set up a multi position tracker +type MultiPositionTrackerSetup struct { Exchange string Asset asset.Item Pair currency.Pair From ebf5fdc7c279d7a2e284ee98ca43559e1ce141d6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 7 Jan 2022 15:28:42 +1100 Subject: [PATCH 055/171] Returns cool changes to original branch --- .../engine_templates/order_manager.tmpl | 1 + .../exchanges_templates/orders.tmpl | 2 + cmd/gctcli/commands.go | 16 + engine/exchange_manager.go | 4 +- engine/order_manager.go | 25 +- engine/order_manager.md | 1 + engine/order_manager_test.go | 33 +- engine/order_manager_types.go | 14 +- engine/rpcserver.go | 7 +- engine/rpcserver_test.go | 14 +- exchanges/btcmarkets/btcmarkets.go | 6 +- exchanges/ftx/ftx.go | 24 +- exchanges/ftx/ftx_test.go | 49 +- exchanges/ftx/ftx_websocket.go | 1 + exchanges/ftx/ftx_websocket_test.go | 2 +- exchanges/ftx/ftx_wrapper.go | 42 +- exchanges/order/futures.go | 70 +- exchanges/order/futures_test.go | 124 +- exchanges/order/futures_types.go | 2 - gctrpc/rpc.pb.go | 1524 +++++++++-------- gctrpc/rpc.proto | 1 + gctrpc/rpc.swagger.json | 6 + gctscript/vm/manager.go | 1 + 23 files changed, 1094 insertions(+), 875 deletions(-) diff --git a/cmd/documentation/engine_templates/order_manager.tmpl b/cmd/documentation/engine_templates/order_manager.tmpl index a7b0e08e675..1aed6f81dcf 100644 --- a/cmd/documentation/engine_templates/order_manager.tmpl +++ b/cmd/documentation/engine_templates/order_manager.tmpl @@ -4,6 +4,7 @@ + The order manager subsystem stores and monitors all orders from enabled exchanges with API keys and `authenticatedSupport` enabled + It can be enabled or disabled via runtime command `-ordermanager=false` and defaults to true + All orders placed via GoCryptoTrader will be added to the order manager store ++ Any futures based order will be tracked via the [futures positions controller](/exchanges/orders/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ### Please click GoDocs chevron above to view current GoDoc information for this package {{template "contributions"}} diff --git a/cmd/documentation/exchanges_templates/orders.tmpl b/cmd/documentation/exchanges_templates/orders.tmpl index 7538fe18615..78bd5db115b 100644 --- a/cmd/documentation/exchanges_templates/orders.tmpl +++ b/cmd/documentation/exchanges_templates/orders.tmpl @@ -7,6 +7,8 @@ - Deletion of order - Order tracking ++ For futures orders, this package also contains a futures position controller. It is responsible for tracking all futures orders, keeping a history of orders and tracking unrealised & realised PNL + ### Please click GoDocs chevron above to view current GoDoc information for this package {{template "contributions"}} {{template "donations" .}} diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index eef898fad87..16391e66655 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4776,6 +4776,11 @@ var getFuturesPositionsCommand = &cli.Command{ Aliases: []string{"v"}, Usage: "includes all orders that make up a position in the response", }, + &cli.BoolFlag{ + Name: "overwrite", + Aliases: []string{"o"}, + Usage: "if true, will overwrite futures results for the provided exchange, asset, pair", + }, }, } @@ -4862,6 +4867,16 @@ func getFuturesPositions(c *cli.Context) error { } } + var overwrite bool + if c.IsSet("overwrite") { + overwrite = c.Bool("overwrite") + } else if c.Args().Get(2) != "" { + overwrite, err = strconv.ParseBool(c.Args().Get(2)) + if err != nil { + return err + } + } + var s, e time.Time s, err = time.Parse(common.SimpleTimeFormat, startTime) if err != nil { @@ -4897,6 +4912,7 @@ func getFuturesPositions(c *cli.Context) error { Status: status, PositionLimit: int64(limit), Verbose: verbose, + Overwrite: overwrite, }) if err != nil { return err diff --git a/engine/exchange_manager.go b/engine/exchange_manager.go index a9dab9ba935..1d0ac43b35d 100644 --- a/engine/exchange_manager.go +++ b/engine/exchange_manager.go @@ -42,7 +42,7 @@ var ( ErrExchangeNotFound = errors.New("exchange not found") ErrExchangeAlreadyLoaded = errors.New("exchange already loaded") ErrExchangeFailedToLoad = errors.New("exchange failed to load") - errExchangeNameIsEmpty = errors.New("exchange name is empty") + ErrExchangeNameIsEmpty = errors.New("exchange name is empty") ) // CustomExchangeBuilder interface allows external applications to create @@ -111,7 +111,7 @@ func (m *ExchangeManager) GetExchangeByName(exchangeName string) (exchange.IBotE return nil, fmt.Errorf("exchange manager: %w", ErrNilSubsystem) } if exchangeName == "" { - return nil, fmt.Errorf("exchange manager: %w", errExchangeNameIsEmpty) + return nil, fmt.Errorf("exchange manager: %w", ErrExchangeNameIsEmpty) } m.m.Lock() defer m.m.Unlock() diff --git a/engine/order_manager.go b/engine/order_manager.go index 9a400d0eedc..b64ee558116 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -225,8 +225,6 @@ func (m *OrderManager) Cancel(ctx context.Context, cancel *order.Cancel) error { return nil } -var errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") - // GetFuturesPositionsForExchange returns futures positions stored within // the order manager's futures position tracker that match the provided params func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]*order.PositionTracker, error) { @@ -246,6 +244,25 @@ func (m *OrderManager) GetFuturesPositionsForExchange(exch string, item asset.It return m.orderStore.futuresPositionController.GetPositionsForExchange(exch, item, pair) } +// ClearFuturesTracking will clear existing futures positions for a given exchange, +// asset, pair for the event that positions have not been tracked accurately +func (m *OrderManager) ClearFuturesTracking(exch string, item asset.Item, pair currency.Pair) error { + if m == nil { + return fmt.Errorf("order manager %w", ErrNilSubsystem) + } + if atomic.LoadInt32(&m.started) == 0 { + return fmt.Errorf("order manager %w", ErrSubSystemNotStarted) + } + if m.orderStore.futuresPositionController == nil { + return errFuturesTrackerNotSetup + } + if !item.IsFutures() { + return fmt.Errorf("%v %w", item, order.ErrNotFutureAsset) + } + + return m.orderStore.futuresPositionController.ClearPositionsForExchange(exch, item, pair) +} + // GetOrderInfo calls the exchange's wrapper GetOrderInfo function // and stores the result in the order manager func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID string, cp currency.Pair, a asset.Item) (order.Detail, error) { @@ -284,7 +301,7 @@ func (m *OrderManager) validate(newOrder *order.Submit) error { } if newOrder.Exchange == "" { - return errExchangeNameIsEmpty + return ErrExchangeNameIsEmpty } if err := newOrder.Validate(); err != nil { @@ -519,8 +536,6 @@ func (m *OrderManager) GetOrdersActive(f *order.Filter) ([]order.Detail, error) return m.orderStore.getActiveOrders(f), nil } -var errUnableToPlaceOrder = errors.New("cannot process order, order not placed") - // processSubmittedOrder adds a new order to the manager func (m *OrderManager) processSubmittedOrder(newOrder *order.Submit, result order.SubmitResponse) (*OrderSubmitResponse, error) { if !result.IsOrderPlaced { diff --git a/engine/order_manager.md b/engine/order_manager.md index 49b82cf101c..bb0161f3636 100644 --- a/engine/order_manager.md +++ b/engine/order_manager.md @@ -22,6 +22,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader + The order manager subsystem stores and monitors all orders from enabled exchanges with API keys and `authenticatedSupport` enabled + It can be enabled or disabled via runtime command `-ordermanager=false` and defaults to true + All orders placed via GoCryptoTrader will be added to the order manager store ++ Any futures based order will be tracked via the [futures positions controller](/exchanges/orders/README.md) which can be used to track PNL. Use GRPC command [getfuturesposition](https://api.gocryptotrader.app/#gocryptotrader_getfuturesposition) to view position data for an exchange, asset, pair ### Please click GoDocs chevron above to view current GoDoc information for this package diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 9816ce00031..78b9581a40d 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -1184,6 +1184,35 @@ func TestGetFuturesPositionsForExchange(t *testing.T) { } } +func TestClearFuturesPositionsForExchange(t *testing.T) { + t.Parallel() + o := &OrderManager{} + cp := currency.NewPair(currency.BTC, currency.USDT) + err := o.ClearFuturesTracking("test", asset.Spot, cp) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + err = o.ClearFuturesTracking("test", asset.Spot, cp) + if !errors.Is(err, errFuturesTrackerNotSetup) { + t.Errorf("received '%v', expected '%v'", err, errFuturesTrackerNotSetup) + } + o.orderStore.futuresPositionController = order.SetupPositionController() + err = o.ClearFuturesTracking("test", asset.Spot, cp) + if !errors.Is(err, order.ErrNotFutureAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFutureAsset) + } + + err = o.ClearFuturesTracking("test", asset.Futures, cp) + if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) + } + o = nil + err = o.ClearFuturesTracking("test", asset.Futures, cp) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } +} func TestSubmitFakeOrder(t *testing.T) { t.Parallel() o := &OrderManager{} @@ -1200,8 +1229,8 @@ func TestSubmitFakeOrder(t *testing.T) { } ord := &order.Submit{} _, err = o.SubmitFakeOrder(ord, resp, false) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", err, errExchangeNameIsEmpty) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", err, ErrExchangeNameIsEmpty) } ord.Exchange = testExchange ord.AssetType = asset.Spot diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index 50dcbae5e27..b8166fd594e 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -14,15 +14,19 @@ const OrderManagerName = "orders" // vars for the fund manager package var ( - orderManagerDelay = time.Second * 10 // ErrOrdersAlreadyExists occurs when the order already exists in the manager ErrOrdersAlreadyExists = errors.New("order already exists") - // ErrOrderNotFound occurs when an order is not found in the orderstore - ErrOrderNotFound = errors.New("order does not exist") - errNilCommunicationsManager = errors.New("cannot start with nil communications manager") // ErrOrderIDCannotBeEmpty occurs when an order does not have an ID ErrOrderIDCannotBeEmpty = errors.New("orderID cannot be empty") - errNilOrder = errors.New("nil order received") + // ErrOrderNotFound occurs when an order is not found in the orderstore + ErrOrderNotFound = errors.New("order does not exist") + + errNilCommunicationsManager = errors.New("cannot start with nil communications manager") + errNilOrder = errors.New("nil order received") + errFuturesTrackerNotSetup = errors.New("futures position tracker not setup") + errUnableToPlaceOrder = errors.New("cannot process order, order not placed") + + orderManagerDelay = time.Second * 10 ) type orderManagerConfig struct { diff --git a/engine/rpcserver.go b/engine/rpcserver.go index f71747f2090..cd89a1d614a 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4150,6 +4150,12 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture sort.Slice(orders, func(i, j int) bool { return orders[i].Date.Before(orders[j].Date) }) + if r.Overwrite { + err = s.OrderManager.ClearFuturesTracking(r.Exchange, a, cp) + if err != nil { + return nil, err + } + } for i := range orders { _, err = s.OrderManager.UpsertOrder(&orders[i]) if err != nil { @@ -4253,7 +4259,6 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if strings.EqualFold(r.SubAccount, ai.Accounts[i].ID) { acc = ai.Accounts[i] break - } } } else if len(ai.Accounts) > 0 { diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 892d65cdaac..858ff86af1f 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -113,7 +113,7 @@ func (f fExchange) FetchAccountInfo(_ context.Context, a asset.Item) (account.Ho } // GetFuturesPositions overrides testExchange's GetFuturesPositions function -func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp currency.Pair, _ time.Time, _ time.Time) ([]order.Detail, error) { +func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp currency.Pair, _, _ time.Time) ([]order.Detail, error) { return []order.Detail{ { Price: 1337, @@ -537,8 +537,8 @@ func TestGetHistoricCandles(t *testing.T) { End: defaultEnd.Format(common.SimpleTimeFormat), AssetType: asset.Spot.String(), }) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", err, errExchangeNameIsEmpty) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", err, ErrExchangeNameIsEmpty) } _, err = s.GetHistoricCandles(context.Background(), &gctrpc.GetHistoricCandlesRequest{ @@ -1113,8 +1113,8 @@ func TestGetOrders(t *testing.T) { AssetType: asset.Spot.String(), Pair: p, }) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", errExchangeNameIsEmpty, err) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", ErrExchangeNameIsEmpty, err) } _, err = s.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{ @@ -1750,8 +1750,8 @@ func TestGetManagedOrders(t *testing.T) { AssetType: asset.Spot.String(), Pair: p, }) - if !errors.Is(err, errExchangeNameIsEmpty) { - t.Errorf("received '%v', expected '%v'", errExchangeNameIsEmpty, err) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Errorf("received '%v', expected '%v'", ErrExchangeNameIsEmpty, err) } _, err = s.GetManagedOrders(context.Background(), &gctrpc.GetOrdersRequest{ diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index b707fdcdf90..7860ac6b2ae 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -28,10 +28,10 @@ const ( btcMarketsAllMarkets = "/markets/" btcMarketsGetTicker = "/ticker/" btcMarketsGetTrades = "/trades?" - btcMarketOrderBooks = "/orderbook?" + btcMarketOrderBook = "/orderbook?" btcMarketsCandles = "/candles?" btcMarketsTickers = "tickers?" - btcMarketsMultipleOrderbooks = "/orderbooks?" + btcMarketsMultipleOrderbooks = "orderbooks?" btcMarketsGetTime = "/time" btcMarketsWithdrawalFees = "/withdrawal-fees" btcMarketsUnauthPath = btcMarketsAPIURL + btcMarketsAPIVersion + btcMarketsAllMarkets @@ -120,7 +120,7 @@ func (b *BTCMarkets) GetOrderbook(ctx context.Context, marketID string, level in if level != 0 { params.Set("level", strconv.FormatInt(level, 10)) } - err := b.SendHTTPRequest(ctx, btcMarketsUnauthPath+marketID+btcMarketOrderBooks+params.Encode(), + err := b.SendHTTPRequest(ctx, btcMarketsUnauthPath+marketID+btcMarketOrderBook+params.Encode(), &temp) if err != nil { return orderbook, err diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index a351c6873b3..1ffbb105ee0 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1476,14 +1476,6 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err return limits, nil } -func (f *FTX) CalculateExpectedPosition(code currency.Code, positionSize float64, side order.Side) (float64, error) { - collateralWeight, ok := f.collateralWeight[code.Upper().String()] - if !ok { - return 0, errCoinMustBeSpecified - } - return collateralWeight.Total * positionSize, nil -} - // LoadCollateralWeightings sets the collateral weights for // currencies supported by FTX func (f *FTX) LoadCollateralWeightings() error { @@ -1649,23 +1641,23 @@ func (c CollateralWeightHolder) isLoaded() bool { } func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { - butts, ok := c[code] + currencyCollateral, ok := c[code] if !ok { - butts = CollateralWeight{Total: weighting} + currencyCollateral = CollateralWeight{Total: weighting} } else { - butts.Total = weighting + currencyCollateral.Total = weighting } - c[code] = butts + c[code] = currencyCollateral } func (c CollateralWeightHolder) loadIMF(code string, imf float64) { - butts, ok := c[code] + currencyCollateral, ok := c[code] if !ok { - butts = CollateralWeight{IMFFactor: imf} + currencyCollateral = CollateralWeight{IMFFactor: imf} } else { - butts.IMFFactor = imf + currencyCollateral.IMFFactor = imf } - c[code] = butts + c[code] = currencyCollateral } func (c CollateralWeightHolder) load(code string, initial, total, imfFactor float64) { diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 323782cfcc5..33704bd86d0 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -277,7 +277,6 @@ func TestGetPositions(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - f.Verbose = true _, err := f.GetPositions(context.Background()) if err != nil { t.Error(err) @@ -743,7 +742,6 @@ func TestGetFills(t *testing.T) { t.Skip() } // optional params - _, err := f.GetFills(context.Background(), spotPair, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) @@ -1598,6 +1596,7 @@ func TestSubaccountBalances(t *testing.T) { } func TestSubaccountTransfer(t *testing.T) { + t.Parallel() tt := []struct { Coin currency.Code Source string @@ -1627,6 +1626,7 @@ func TestSubaccountTransfer(t *testing.T) { } func TestGetStakes(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1637,6 +1637,7 @@ func TestGetStakes(t *testing.T) { } func TestGetUnstakeRequests(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1647,6 +1648,7 @@ func TestGetUnstakeRequests(t *testing.T) { } func TestGetStakeBalances(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1657,6 +1659,7 @@ func TestGetStakeBalances(t *testing.T) { } func TestUnstakeRequest(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set") } @@ -1672,6 +1675,7 @@ func TestUnstakeRequest(t *testing.T) { } func TestCancelUnstakeRequest(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set") } @@ -1682,6 +1686,7 @@ func TestCancelUnstakeRequest(t *testing.T) { } func TestGetStakingRewards(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1692,6 +1697,7 @@ func TestGetStakingRewards(t *testing.T) { } func TestStakeRequest(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isn't set") } @@ -1704,6 +1710,7 @@ func TestStakeRequest(t *testing.T) { } func TestUpdateOrderExecutionLimits(t *testing.T) { + t.Parallel() err := f.UpdateOrderExecutionLimits(context.Background(), "") if err != nil { t.Fatal(err) @@ -1730,6 +1737,7 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { } func TestScaleCollateral(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1800,12 +1808,16 @@ func TestScaleCollateral(t *testing.T) { } } } + if accountInfo.Collateral == 0 { + return + } if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } } func TestCalculateTotalCollateral(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1853,7 +1865,7 @@ func TestCalculateTotalCollateral(t *testing.T) { if err != nil { t.Error(err) } - if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { + if accountInfo.Collateral != 0 && (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral)*100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } @@ -1867,12 +1879,15 @@ func TestCalculateTotalCollateral(t *testing.T) { } func TestCalculatePNL(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - f.Verbose = true pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) positions, err := f.GetFuturesPositions(context.Background(), asset.Futures, pair, time.Date(2021, 1, 6, 4, 28, 0, 0, time.UTC), time.Date(2021, 12, 31, 4, 32, 0, 0, time.UTC)) + if err != nil { + t.Error(err) + } var orders []order.Detail for i := range positions { orders = append(orders, order.Detail{ @@ -1918,6 +1933,7 @@ func TestCalculatePNL(t *testing.T) { } func TestGetFuturesPositions(t *testing.T) { + t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } @@ -1930,3 +1946,28 @@ func TestGetFuturesPositions(t *testing.T) { t.Error(err) } } + +func TestLoadCollateralWeightings(t *testing.T) { + t.Parallel() + ff := FTX{} + err := ff.LoadCollateralWeightings() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(ff.collateralWeight) == 0 { + t.Fatal("expected some weight") + } + if !ff.collateralWeight.isLoaded() { + t.Error("expected loaded weight") + } + if !areTestAPIKeysSet() { + return + } + err = f.LoadCollateralWeightings() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(f.collateralWeight) == 0 { + t.Fatal("expected some weight") + } +} diff --git a/exchanges/ftx/ftx_websocket.go b/exchanges/ftx/ftx_websocket.go index 311b39bd8e8..2c2c6fa42d5 100644 --- a/exchanges/ftx/ftx_websocket.go +++ b/exchanges/ftx/ftx_websocket.go @@ -563,6 +563,7 @@ func (f *FTX) WsProcessPartialOB(data *WsOrderbookData, p currency.Pair, a asset Exchange: f.Name, VerifyOrderbook: f.CanVerifyOrderbook, } + return f.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index f6787fb49d6..5492ca19006 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -359,7 +359,7 @@ func TestParsingMarketsData(t *testing.T) { "future": { "name": "ADA-0626", "underlying": "ADA", - "description": "Cardano June 2020 FuturesTracker", + "description": "Cardano June 2020 Futures", "type": "future", "expiry": "2020-06-26T003:00:00+00:00", "perpetual": false, "expired": false, diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 213cacfef11..8eace2ef2b1 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -186,22 +186,20 @@ func (f *FTX) Setup(exch *config.Exchange) error { return err } - if exch.Websocket != nil && *exch.Websocket { - err = f.Websocket.Setup(&stream.WebsocketSetup{ - ExchangeConfig: exch, - DefaultURL: ftxWSURL, - RunningURL: wsEndpoint, - Connector: f.WsConnect, - Subscriber: f.Subscribe, - Unsubscriber: f.Unsubscribe, - GenerateSubscriptions: f.GenerateDefaultSubscriptions, - Features: &f.Features.Supports.WebsocketCapabilities, - TradeFeed: f.Features.Enabled.TradeFeed, - FillsFeed: f.Features.Enabled.FillsFeed, - }) - if err != nil { - return err - } + err = f.Websocket.Setup(&stream.WebsocketSetup{ + ExchangeConfig: exch, + DefaultURL: ftxWSURL, + RunningURL: wsEndpoint, + Connector: f.WsConnect, + Subscriber: f.Subscribe, + Unsubscriber: f.Unsubscribe, + GenerateSubscriptions: f.GenerateDefaultSubscriptions, + Features: &f.Features.Supports.WebsocketCapabilities, + TradeFeed: f.Features.Enabled.TradeFeed, + FillsFeed: f.Features.Enabled.FillsFeed, + }) + if err != nil { + return err } if err = f.CurrencyPairs.IsAssetEnabled(asset.Futures); err == nil { @@ -213,13 +211,10 @@ func (f *FTX) Setup(exch *config.Exchange) error { err) } } - if exch.Websocket != nil && *exch.Websocket { - return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - }) - } - return nil + return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + }) } // Start starts the FTX go routine @@ -264,7 +259,6 @@ func (f *FTX) Run() { f.Name, err) } - } // FetchTradablePairs returns a list of the exchanges tradable pairs diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 7a9488c4948..b3b05121608 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -81,9 +81,49 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite if !ok { return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) } + return multiPositionTracker.GetPositions(), nil } +// ClearPositionsForExchange resets positions for an +// exchange, asset, pair that has been stored +func (c *PositionController) ClearPositionsForExchange(exch string, item asset.Item, pair currency.Pair) error { + if c == nil { + return common.ErrNilPointer + } + c.m.Lock() + defer c.m.Unlock() + if !item.IsFutures() { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFutureAsset) + } + exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + if !ok { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) + } + itemM, ok := exchM[item] + if !ok { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForAsset) + } + multiPositionTracker, ok := itemM[pair] + if !ok { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) + } + newMPT, err := SetupMultiPositionTracker(&MultiPositionTrackerSetup{ + Exchange: exch, + Asset: item, + Pair: pair, + Underlying: multiPositionTracker.underlying, + OfflineCalculation: multiPositionTracker.offlinePNLCalculation, + UseExchangePNLCalculation: multiPositionTracker.useExchangePNLCalculations, + ExchangePNLCalculation: multiPositionTracker.exchangePNLCalculation, + }) + if err != nil { + return err + } + c.positionTrackerControllers[strings.ToLower(exch)][item][pair] = newMPT + return nil +} + // SetupMultiPositionTracker creates a futures order tracker for a specific exchange func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPositionTracker, error) { if setup == nil { @@ -313,7 +353,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if !p.contractPair.Equal(d.Pair) { return fmt.Errorf("%w pair '%v' received: '%v'", errOrderNotEqualToTracker, d.Pair, p.contractPair) } - if p.exchange != strings.ToLower(d.Exchange) { + if !strings.EqualFold(p.exchange, d.Exchange) { return fmt.Errorf("%w exchange '%v' received: '%v'", errOrderNotEqualToTracker, d.Exchange, p.exchange) } if p.asset != d.AssetType { @@ -354,18 +394,15 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } else { p.longPositions = append(p.longPositions, d.Copy()) } - var shortSide, longSide, averageLeverage decimal.Decimal + var shortSide, longSide decimal.Decimal for i := range p.shortPositions { shortSide = shortSide.Add(decimal.NewFromFloat(p.shortPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(p.shortPositions[i].Leverage) } for i := range p.longPositions { longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount)) - averageLeverage = decimal.NewFromFloat(p.longPositions[i].Leverage) } - averageLeverage.Div(decimal.NewFromInt(int64(len(p.shortPositions))).Add(decimal.NewFromInt(int64(len(p.longPositions))))) if p.currentDirection == "" { p.currentDirection = d.Side } @@ -400,9 +437,10 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { cal.Exposure.LessThan(amount) { // latest order swaps directions! // split the order to calculate PNL from each direction - first := amount.Sub(cal.Exposure) - second := cal.Exposure.Sub(amount).Abs() - cal.Fee = cal.Fee.Div(decimal.NewFromInt(2)) + first := cal.Exposure + second := amount.Sub(cal.Exposure) + baseFee := cal.Fee.Div(amount) + cal.Fee = baseFee.Mul(first) cal.Amount = first result, err = p.PNLCalculation.CalculatePNL(cal) if err != nil { @@ -423,12 +461,12 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.openingDirection = Long } + cal.Fee = baseFee.Mul(second) cal.Amount = second cal.EntryPrice = price cal.Time = cal.Time.Add(1) cal.PNLHistory = p.pnlHistory result, err = p.PNLCalculation.CalculatePNL(cal) - } else { result, err = p.PNLCalculation.CalculatePNL(cal) } @@ -446,18 +484,21 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } p.unrealisedPNL = result.UnrealisedPNL - if longSide.GreaterThan(shortSide) { + switch { + case longSide.GreaterThan(shortSide): p.currentDirection = Long - } else if shortSide.GreaterThan(longSide) { + case shortSide.GreaterThan(longSide): p.currentDirection = Short - } else { + default: p.currentDirection = UnknownSide } + if p.currentDirection.IsLong() { p.exposure = longSide.Sub(shortSide) } else { p.exposure = shortSide.Sub(longSide) } + if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) @@ -476,9 +517,6 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { // CalculatePNL this is a localised generic way of calculating open // positions' worth, it is an implementation of the PNLCalculation interface -// -// do not use any properties of p, use calc, otherwise there will be -// sync issues func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, error) { if calc == nil { return nil, ErrNilPNLCalculator @@ -537,6 +575,8 @@ func (p *PNLCalculator) CalculatePNL(calc *PNLCalculatorRequest) (*PNLResult, er return response, nil } +// calculateRealisedPNL calculates the total realised PNL +// based on PNL history, minus fees func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 4bc0d815ac4..69553a0da8a 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -11,11 +11,15 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) +const testExchange = "test" + +// FakePNL is implements PNL interface type FakePNL struct { err error result *PNLResult } +// CalculatePNL overrides default pnl calculations func (f *FakePNL) CalculatePNL(*PNLCalculatorRequest) (*PNLResult, error) { if f.err != nil { return nil, f.err @@ -55,14 +59,14 @@ func TestUpsertPNLEntry(t *testing.T) { func TestTrackNewOrder(t *testing.T) { t.Parallel() - exch := "test" + exch := testExchange item := asset.Futures pair, err := currency.NewPairFromStrings("BTC", "1231") if !errors.Is(err, nil) { t.Error(err) } e := MultiPositionTracker{ - exchange: "test", + exchange: testExchange, exchangePNLCalculation: &FakePNL{}, } setup := &PositionTrackerSetup{ @@ -121,6 +125,7 @@ func TestTrackNewOrder(t *testing.T) { t.Error("expected 1") } + od.Date = od.Date.Add(1) od.Amount = 0.4 od.Side = Short od.ID = "3" @@ -137,9 +142,12 @@ func TestTrackNewOrder(t *testing.T) { if f.exposure.InexactFloat64() != 0.6 { t.Error("expected 0.6") } + + od.Date = od.Date.Add(1) od.Amount = 0.8 od.Side = Short od.ID = "4" + od.Fee = 0.1 err = f.TrackNewOrder(od) if !errors.Is(err, nil) { t.Error(err) @@ -151,6 +159,7 @@ func TestTrackNewOrder(t *testing.T) { t.Errorf("expected %v received %v", 0.2, f.exposure) } + od.Date = od.Date.Add(1) od.ID = "5" od.Side = Long od.Amount = 0.2 @@ -190,7 +199,7 @@ func TestSetupMultiPositionTracker(t *testing.T) { if !errors.Is(err, errExchangeNameEmpty) { t.Error(err) } - setup.Exchange = "test" + setup.Exchange = testExchange _, err = SetupMultiPositionTracker(setup) if !errors.Is(err, ErrNotFutureAsset) { t.Error(err) @@ -224,14 +233,14 @@ func TestSetupMultiPositionTracker(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if resp.exchange != "test" { + if resp.exchange != testExchange { t.Errorf("expected 'test' received %v", resp.exchange) } } func TestExchangeTrackNewOrder(t *testing.T) { t.Parallel() - exch := "test" + exch := testExchange item := asset.Futures pair := currency.NewPair(currency.BTC, currency.USDT) setup := &MultiPositionTrackerSetup{ @@ -309,6 +318,9 @@ func TestExchangeTrackNewOrder(t *testing.T) { ID: "2", Amount: 2, }) + if !errors.Is(err, nil) { + t.Error(err) + } if len(resp.positions) != 2 { t.Errorf("expected '2' received %v", len(resp.positions)) } @@ -411,7 +423,7 @@ func TestGetStats(t *testing.T) { t.Error("expected 0") } - p.exchange = "test" + p.exchange = testExchange stats = p.GetStats() if stats.Exchange != p.exchange { t.Errorf("expected '%v' received '%v'", p.exchange, stats.Exchange) @@ -433,13 +445,13 @@ func TestGetPositions(t *testing.T) { } p.positions = append(p.positions, &PositionTracker{ - exchange: "test", + exchange: testExchange, }) positions = p.GetPositions() if len(positions) != 1 { t.Fatal("expected 1") } - if positions[0].exchange != "test" { + if positions[0].exchange != testExchange { t.Error("expected 'test'") } @@ -448,14 +460,13 @@ func TestGetPositions(t *testing.T) { if len(positions) > 0 { t.Error("expected 0") } - } func TestGetPositionsForExchange(t *testing.T) { t.Parallel() c := &PositionController{} p := currency.NewPair(currency.BTC, currency.USDT) - pos, err := c.GetPositionsForExchange("test", asset.Futures, p) + pos, err := c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } @@ -463,54 +474,106 @@ func TestGetPositionsForExchange(t *testing.T) { t.Error("expected zero") } c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers["test"] = nil - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + c.positionTrackerControllers[testExchange] = nil + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForAsset) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - c.positionTrackerControllers["test"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers["test"][asset.Futures] = nil - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + c.positionTrackerControllers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures] = nil + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForPair) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) } - pos, err = c.GetPositionsForExchange("test", asset.Spot, p) + _, err = c.GetPositionsForExchange(testExchange, asset.Spot, p) if !errors.Is(err, ErrNotFutureAsset) { t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) } - c.positionTrackerControllers["test"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ - exchange: "test", + c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, } - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + pos, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v", err, nil) } if len(pos) != 0 { t.Fatal("expected zero") } - c.positionTrackerControllers["test"][asset.Futures][p] = &MultiPositionTracker{ - exchange: "test", + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, positions: []*PositionTracker{ { - exchange: "test", + exchange: testExchange, }, }, } - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + pos, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v", err, nil) } if len(pos) != 1 { t.Fatal("expected 1") } - if pos[0].exchange != "test" { + if pos[0].exchange != testExchange { t.Error("expected test") } c = nil - pos, err = c.GetPositionsForExchange("test", asset.Futures, p) + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } +} + +func TestClearPositionsForExchange(t *testing.T) { + t.Parallel() + c := &PositionController{} + p := currency.NewPair(currency.BTC, currency.USDT) + err := c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange] = nil + err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForAsset) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + c.positionTrackerControllers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures] = nil + err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, ErrPositionsNotLoadedForPair) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) + } + err = c.ClearPositionsForExchange(testExchange, asset.Spot, p) + if !errors.Is(err, ErrNotFutureAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFutureAsset) + } + + c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, + } + c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + exchange: testExchange, + underlying: currency.DOGE, + positions: []*PositionTracker{ + { + exchange: testExchange, + }, + }, + } + err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if len(c.positionTrackerControllers[testExchange][asset.Futures][p].positions) != 0 { + t.Fatal("expected 0") + } + c = nil + _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, common.ErrNilPointer) { t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) } @@ -556,7 +619,7 @@ func TestSetupPositionTracker(t *testing.T) { if p != nil { t.Error("expected nil") } - m.exchange = "test" + m.exchange = testExchange p, err = m.SetupPositionTracker(nil) if !errors.Is(err, errNilSetup) { t.Errorf("received '%v' expected '%v", err, errNilSetup) @@ -594,13 +657,13 @@ func TestSetupPositionTracker(t *testing.T) { t.Errorf("received '%v' expected '%v", err, nil) } if p == nil { - t.Error("expected nil") + t.Fatal("expected not nil") } - if p.exchange != "test" { + if p.exchange != testExchange { t.Error("expected test") } - p, err = m.SetupPositionTracker(&PositionTrackerSetup{ + _, err = m.SetupPositionTracker(&PositionTrackerSetup{ Asset: asset.Futures, Pair: cp, UseExchangePNLCalculation: true, @@ -641,7 +704,6 @@ func TestCalculatePNL(t *testing.T) { if !errors.Is(err, errCannotCalculateUnrealisedPNL) { t.Errorf("received '%v' expected '%v", err, errCannotCalculateUnrealisedPNL) } - } func TestTrackPNLByTime(t *testing.T) { diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 4052dca6576..185c8f5895a 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -96,7 +96,6 @@ type MultiPositionTracker struct { // order positions allows for an easier time knowing which order is // part of which position tracker orderPositions map[string]*PositionTracker - pnl decimal.Decimal offlinePNLCalculation bool useExchangePNLCalculations bool exchangePNLCalculation PNLCalculation @@ -131,7 +130,6 @@ type PositionTracker struct { currentDirection Side openingDirection Side status Status - averageLeverage decimal.Decimal unrealisedPNL decimal.Decimal realisedPNL decimal.Decimal shortPositions []Detail diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 7ef71ba2121..ddef4eaffd2 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -10778,6 +10778,7 @@ type GetFuturesPositionsRequest struct { Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` Verbose bool `protobuf:"varint,8,opt,name=verbose,proto3" json:"verbose,omitempty"` + Overwrite bool `protobuf:"varint,9,opt,name=overwrite,proto3" json:"overwrite,omitempty"` } func (x *GetFuturesPositionsRequest) Reset() { @@ -10868,6 +10869,13 @@ func (x *GetFuturesPositionsRequest) GetVerbose() bool { return false } +func (x *GetFuturesPositionsRequest) GetOverwrite() bool { + if x != nil { + return x.Overwrite + } + return false +} + type GetFuturesPositionsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -12779,7 +12787,7 @@ var file_rpc_proto_rawDesc = []byte{ 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x1a, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa8, 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, @@ -12796,785 +12804,787 @@ var file_rpc_proto_rawDesc = []byte{ 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, - 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, - 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, - 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, - 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, - 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, - 0x4e, 0x4c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, - 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x24, 0x0a, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, - 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, - 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, - 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, - 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, - 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, - 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, - 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, - 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, - 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x12, 0x4b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, - 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, - 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, - 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, - 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, - 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, - 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, - 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, - 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, - 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, - 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, - 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, - 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, - 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, - 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0xed, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, + 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, + 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, + 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, + 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, + 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, + 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, + 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, + 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, + 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, + 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, + 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, + 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, + 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2a, + 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, + 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x4b, + 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, + 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x8b, 0x01, 0x0a, 0x15, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, + 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, + 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, + 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, + 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, + 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, + 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, + 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, + 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, - 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, + 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, + 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, - 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, - 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, - 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, - 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, - 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, - 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, - 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, + 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, - 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, - 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, - 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, + 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, + 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, + 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, + 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, + 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, - 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, - 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, - 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, - 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, - 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, - 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, - 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, + 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, + 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, + 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, + 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, + 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, + 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, - 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, - 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, - 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, - 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, + 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, + 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, + 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, + 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, + 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, + 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, - 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, - 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, - 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, - 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, - 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, - 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, - 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, + 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, + 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, + 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, - 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, - 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, + 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, + 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, + 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, - 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, - 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, + 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, + 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, + 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, + 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, + 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, - 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, - 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, - 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, + 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, + 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, + 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, + 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, + 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, + 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, + 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, + 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, - 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, - 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, + 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, + 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 601cedad4bf..86b123a51fb 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1032,6 +1032,7 @@ message GetFuturesPositionsRequest { string status = 6; int64 positionLimit = 7; bool verbose = 8; + bool overwrite = 9; } message GetFuturesPositionsResponse { diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index a336d1b6f03..8f58bcd38d0 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -1837,6 +1837,12 @@ "in": "query", "required": false, "type": "boolean" + }, + { + "name": "overwrite", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ diff --git a/gctscript/vm/manager.go b/gctscript/vm/manager.go index 294b5d25dad..f29fec27491 100644 --- a/gctscript/vm/manager.go +++ b/gctscript/vm/manager.go @@ -16,6 +16,7 @@ const ( Name = "gctscript" ) +// ErrNilSubsystem returned when script manager has not been set up var ErrNilSubsystem = errors.New("gct script has not been set up") // GctScriptManager loads and runs GCT Tengo scripts From 33c5c907f2bde8370ae220a4265081869e908bd3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 11 Jan 2022 16:53:44 +1100 Subject: [PATCH 056/171] end of day fixes --- backtester/eventhandlers/exchange/exchange.go | 9 ++- .../eventhandlers/exchange/exchange_types.go | 10 ++- .../eventhandlers/portfolio/portfolio.go | 81 ++++++++++++------- .../eventhandlers/portfolio/portfolio_test.go | 55 +++++++------ .../portfolio/portfolio_types.go | 2 +- 5 files changed, 89 insertions(+), 68 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index c10cbea450a..8cd1b81304d 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -3,6 +3,7 @@ package exchange import ( "context" "fmt" + "strings" "github.com/gofrs/uuid" "github.com/shopspring/decimal" @@ -339,8 +340,8 @@ func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.D } // SetExchangeAssetCurrencySettings sets the settings for an exchange, asset, currency -func (e *Exchange) SetExchangeAssetCurrencySettings(exch string, a asset.Item, cp currency.Pair, c *Settings) { - if c.Exchange == "" || +func (e *Exchange) SetExchangeAssetCurrencySettings(a asset.Item, cp currency.Pair, c *Settings) { + if c.Exchange == nil || c.Asset == "" || c.Pair.IsEmpty() { return @@ -349,7 +350,7 @@ func (e *Exchange) SetExchangeAssetCurrencySettings(exch string, a asset.Item, c for i := range e.CurrencySettings { if e.CurrencySettings[i].Pair == cp && e.CurrencySettings[i].Asset == a && - exch == e.CurrencySettings[i].Exchange { + strings.EqualFold(c.Exchange.GetName(), e.CurrencySettings[i].Exchange.GetName()) { e.CurrencySettings[i] = *c return } @@ -362,7 +363,7 @@ func (e *Exchange) GetCurrencySettings(exch string, a asset.Item, cp currency.Pa for i := range e.CurrencySettings { if e.CurrencySettings[i].Pair.Equal(cp) { if e.CurrencySettings[i].Asset == a { - if exch == e.CurrencySettings[i].Exchange { + if strings.EqualFold(exch, e.CurrencySettings[i].Exchange.GetName()) { return e.CurrencySettings[i], nil } } diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index 6a502cbdb03..e1b83b5258d 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -10,6 +10,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/engine" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -36,7 +37,7 @@ type Exchange struct { // Settings allow the eventhandler to size an order within the limitations set by the config file type Settings struct { - Exchange string + Exchange exchange.IBotExchange UseRealOrders bool Pair currency.Pair @@ -54,9 +55,10 @@ type Settings struct { MinimumSlippageRate decimal.Decimal MaximumSlippageRate decimal.Decimal - Limits *gctorder.Limits - CanUseExchangeLimits bool - SkipCandleVolumeFitting bool + Limits *gctorder.Limits + CanUseExchangeLimits bool + SkipCandleVolumeFitting bool + UseExchangePNLCalculation bool } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index f8b3e96acee..1f0a248c128 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strings" "time" "github.com/shopspring/decimal" @@ -18,7 +19,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" - gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" @@ -49,11 +49,11 @@ func (p *Portfolio) Reset() { } // SetupCurrencySettingsMap ensures a map is created and no panics happen -func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch gctexchange.IBotExchange) error { +func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings) error { if settings == nil { return errNoPortfolioSettings } - if settings.Exchange == "" { + if settings.Exchange == nil { return errExchangeUnset } if settings.Asset == "" { @@ -65,17 +65,33 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch g if p.exchangeAssetPairSettings == nil { p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) } - if p.exchangeAssetPairSettings[settings.Exchange] == nil { - p.exchangeAssetPairSettings[settings.Exchange] = make(map[asset.Item]map[currency.Pair]*Settings) + name := strings.ToLower(settings.Exchange.GetName()) + if p.exchangeAssetPairSettings[name] == nil { + p.exchangeAssetPairSettings[name] = make(map[asset.Item]map[currency.Pair]*Settings) } - if p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] == nil { - p.exchangeAssetPairSettings[settings.Exchange][settings.Asset] = make(map[currency.Pair]*Settings) + if p.exchangeAssetPairSettings[name][settings.Asset] == nil { + p.exchangeAssetPairSettings[name][settings.Asset] = make(map[currency.Pair]*Settings) } - if _, ok := p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair]; ok { + if _, ok := p.exchangeAssetPairSettings[name][settings.Asset][settings.Pair]; ok { return nil } - p.exchangeAssetPairSettings[settings.Exchange][settings.Asset][settings.Pair] = &Settings{ + futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ + Exchange: name, + Asset: settings.Asset, + Pair: settings.Pair, + Underlying: settings.Pair.Base, + OfflineCalculation: true, + UseExchangePNLCalculation: settings.UseExchangePNLCalculation, + } + if settings.UseExchangePNLCalculation { + futureTrackerSetup.ExchangePNLCalculation = settings.Exchange + } + tracker, err := gctorder.SetupMultiPositionTracker(futureTrackerSetup) + if err != nil { + return err + } + p.exchangeAssetPairSettings[name][settings.Asset][settings.Pair] = &Settings{ Fee: settings.ExchangeFee, BuySideSizing: settings.BuySide, SellSideSizing: settings.SellSide, @@ -83,8 +99,8 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings, exch g ComplianceManager: compliance.Manager{ Snapshots: []compliance.Snapshot{}, }, - Exchange: exch, - FuturesTracker: gctorder.SetupPositionController(), + Exchange: settings.Exchange, + FuturesTracker: tracker, } return nil } @@ -575,21 +591,23 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er if settings.FuturesTracker == nil { return nil, errors.New("no futures tracker") } - positions, err := settings.FuturesTracker.GetPositionsForExchange(e.GetExchange(), e.GetAssetType(), e.Pair()) - if err != nil { - return nil, err + + response := &PNLSummary{ + Exchange: e.GetExchange(), + Item: e.GetAssetType(), + Pair: e.Pair(), + } + positions := settings.FuturesTracker.GetPositions() + if len(positions) == 0 { + return response, nil } pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() if err != nil { return nil, err } - return &PNLSummary{ - Exchange: e.GetExchange(), - Item: e.GetAssetType(), - Pair: e.Pair(), - PNL: pnl, - }, nil + response.PNL = pnl + return response, nil } // GetLatestPNLs returns all PNL details in one array @@ -607,20 +625,21 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { if settings.FuturesTracker == nil { continue } - positions, err := settings.FuturesTracker.GetPositionsForExchange(exchK, assetK, pairK) - if err != nil { - return nil - } - pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() - if err != nil { - continue - } - result = append(result, PNLSummary{ + summary := PNLSummary{ Exchange: exchK, Item: assetK, Pair: pairK, - PNL: pnl, - }) + } + positions := settings.FuturesTracker.GetPositions() + if len(positions) > 0 { + pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() + if err != nil { + continue + } + summary.PNL = pnl + } + + result = append(result, summary) } } } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 5a5efaae98a..7e3f5489e48 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -20,6 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -66,27 +67,27 @@ func TestSetup(t *testing.T) { func TestSetupCurrencySettingsMap(t *testing.T) { t.Parallel() p := &Portfolio{} - err := p.SetupCurrencySettingsMap(nil, nil) + err := p.SetupCurrencySettingsMap(nil) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{}) if !errors.Is(err, errExchangeUnset) { t.Errorf("received: %v, expected: %v", err, errExchangeUnset) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi"}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}}) if !errors.Is(err, errAssetUnset) { t.Errorf("received: %v, expected: %v", err, errAssetUnset) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot}) if !errors.Is(err, errCurrencyPairUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyPairUnset) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -107,7 +108,7 @@ func TestSetHoldings(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -147,7 +148,7 @@ func TestGetLatestHoldingsForAllCurrencies(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -209,7 +210,7 @@ func TestViewHoldingAtTimePeriod(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoHoldings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -282,7 +283,7 @@ func TestUpdate(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -308,7 +309,7 @@ func TestGetFee(t *testing.T) { t.Error("expected 0") } - err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -328,7 +329,7 @@ func TestGetComplianceManager(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -355,7 +356,7 @@ func TestAddComplianceSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -401,7 +402,7 @@ func TestOnFill(t *testing.T) { if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -478,7 +479,7 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "hi", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -525,7 +526,7 @@ func TestOnSignal(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -572,7 +573,7 @@ func TestGetSnapshotAtTime(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } cp := currency.NewPair(currency.XRP, currency.DOGE) - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "exch", Asset: asset.Spot, Pair: cp}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: cp}) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -626,11 +627,10 @@ func TestGetLatestSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } cp := currency.NewPair(currency.XRP, currency.DOGE) - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: "exch", Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}, nil) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } - tt := time.Now() s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][cp] if !ok { t.Fatal("couldn't get settings") @@ -692,7 +692,7 @@ func TestCalculatePNL(t *testing.T) { } ev := &kline.Kline{} - err := p.CalculatePNL(ev, nil) + err := p.CalculatePNL(ev) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } @@ -701,11 +701,11 @@ func TestCalculatePNL(t *testing.T) { a := asset.Futures pair, _ := currency.NewPairFromStrings("BTC", "1231") err = p.SetupCurrencySettingsMap(&exchange.Settings{ - Exchange: exch, + Exchange: &ftx.FTX{}, UseRealOrders: false, Pair: pair, Asset: a, - }, nil) + }) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -716,7 +716,7 @@ func TestCalculatePNL(t *testing.T) { ev.CurrencyPair = pair ev.Time = tt0 - err = p.CalculatePNL(ev, nil) + err = p.CalculatePNL(ev) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -738,12 +738,11 @@ func TestCalculatePNL(t *testing.T) { t.Fatal("couldn't get settings") } ev.Close = decimal.NewFromInt(1337) - err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ - { - ClosePrice: decimal.NewFromInt(1336), - FuturesOrder: futuresOrder, - }, - }, tt0, 0, false) + err = s.ComplianceManager.AddSnapshot(&compliance.Snapshot{ + Offset: 0, + Timestamp: time.Time{}, + Orders: nil, + }, false) err = p.CalculatePNL(ev, nil) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index d20da2520fc..07ebf848368 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -83,7 +83,7 @@ type Settings struct { HoldingsSnapshots []holdings.Holding ComplianceManager compliance.Manager Exchange gctexchange.IBotExchange - FuturesTracker *gctorder.PositionController + FuturesTracker *gctorder.MultiPositionTracker } // PNLSummary holds a PNL result along with From 5e1942e5cf86f325a7a5af11fe5f077582b19908 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 13 Jan 2022 15:45:18 +1100 Subject: [PATCH 057/171] Fixing some tests --- backtester/backtest/backtest.go | 4 +- .../eventhandlers/exchange/exchange_test.go | 34 ++++++++--- .../eventhandlers/exchange/exchange_types.go | 2 +- .../portfolio/compliance/compliance_test.go | 59 ++++++++++++------- .../eventhandlers/portfolio/risk/risk.go | 2 +- .../portfolio/risk/risk_types.go | 2 +- 6 files changed, 68 insertions(+), 35 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index e1dff5daada..2e8b9b4b38d 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -440,7 +440,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, bt.Exchange = &e for i := range e.CurrencySettings { - err = p.SetupCurrencySettingsMap(&e.CurrencySettings[i], emm[e.CurrencySettings[i].Exchange]) + err = p.SetupCurrencySettingsMap(&e.CurrencySettings[i]) if err != nil { return nil, err } @@ -561,7 +561,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange } } resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ - Exchange: cfg.CurrencySettings[i].ExchangeName, + Exchange: exch, MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, Pair: pair, diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 5c1bc6f0f94..f31c157583f 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -13,18 +13,35 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" + "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/engine" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) -const testExchange = "binance" +const testExchange = "ftx" type fakeFund struct{} +func (f *fakeFund) GetPairReader() (funding.IPairReader, error) { + return nil, nil +} + +func (f *fakeFund) GetCollateralReader() (funding.ICollateralReader, error) { + return nil, nil +} + +func (f *fakeFund) GetPairReleaser() (funding.IPairReleaser, error) { + return nil, nil +} +func (f *fakeFund) GetCollateralReleaser() (funding.ICollateralReleaser, error) { + return nil, nil +} + func (f *fakeFund) IncreaseAvailable(decimal.Decimal, gctorder.Side) {} func (f *fakeFund) Release(decimal.Decimal, decimal.Decimal, gctorder.Side) error { return nil @@ -44,12 +61,12 @@ func TestReset(t *testing.T) { func TestSetCurrency(t *testing.T) { t.Parallel() e := Exchange{} - e.SetExchangeAssetCurrencySettings("", "", currency.Pair{}, &Settings{}) + e.SetExchangeAssetCurrencySettings("", currency.Pair{}, &Settings{}) if len(e.CurrencySettings) != 0 { t.Error("expected 0") } cs := &Settings{ - Exchange: testExchange, + Exchange: &ftx.FTX{}, UseRealOrders: true, Pair: currency.NewPair(currency.BTC, currency.USDT), Asset: asset.Spot, @@ -62,7 +79,7 @@ func TestSetCurrency(t *testing.T) { MinimumSlippageRate: decimal.Zero, MaximumSlippageRate: decimal.Zero, } - e.SetExchangeAssetCurrencySettings(testExchange, asset.Spot, currency.NewPair(currency.BTC, currency.USDT), cs) + e.SetExchangeAssetCurrencySettings(asset.Spot, currency.NewPair(currency.BTC, currency.USDT), cs) result, err := e.GetCurrencySettings(testExchange, asset.Spot, currency.NewPair(currency.BTC, currency.USDT)) if err != nil { t.Error(err) @@ -70,7 +87,7 @@ func TestSetCurrency(t *testing.T) { if !result.UseRealOrders { t.Error("expected true") } - e.SetExchangeAssetCurrencySettings(testExchange, asset.Spot, currency.NewPair(currency.BTC, currency.USDT), cs) + e.SetExchangeAssetCurrencySettings(asset.Spot, currency.NewPair(currency.BTC, currency.USDT), cs) if len(e.CurrencySettings) != 1 { t.Error("expected 1") } @@ -231,7 +248,7 @@ func TestExecuteOrder(t *testing.T) { } cs := Settings{ - Exchange: testExchange, + Exchange: &ftx.FTX{}, UseRealOrders: false, Pair: p, Asset: a, @@ -282,9 +299,8 @@ func TestExecuteOrder(t *testing.T) { t.Error(err) } d.Next() - _, err = e.ExecuteOrder(o, d, bot.OrderManager, &fakeFund{}) - if err != nil { + if err != errNilCurrencySettings { t.Error(err) } @@ -345,7 +361,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { } cs := Settings{ - Exchange: testExchange, + Exchange: &ftx.FTX{}, UseRealOrders: false, Pair: p, Asset: a, diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index e1b83b5258d..f9df249ae97 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -24,7 +24,7 @@ var ( // ExecutionHandler interface dictates what functions are required to submit an order type ExecutionHandler interface { - SetExchangeAssetCurrencySettings(string, asset.Item, currency.Pair, *Settings) + SetExchangeAssetCurrencySettings(asset.Item, currency.Pair, *Settings) GetCurrencySettings(string, asset.Item, currency.Pair) (Settings, error) ExecuteOrder(order.Event, data.Handler, *engine.OrderManager, funding.IFundReleaser) (*fill.Fill, error) Reset() diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_test.go b/backtester/eventhandlers/portfolio/compliance/compliance_test.go index 6508d5b7ddf..a65ffdee77a 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_test.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_test.go @@ -6,37 +6,57 @@ import ( "time" "github.com/shopspring/decimal" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) func TestAddSnapshot(t *testing.T) { t.Parallel() m := Manager{} tt := time.Now() - err := m.AddSnapshot([]SnapshotOrder{}, tt, 1, true) + err := m.AddSnapshot(&Snapshot{}, true) if !errors.Is(err, errSnapshotNotFound) { t.Errorf("received: %v, expected: %v", err, errSnapshotNotFound) } - err = m.AddSnapshot([]SnapshotOrder{}, tt, 1, false) + err = m.AddSnapshot(&Snapshot{ + Offset: 0, + Timestamp: tt, + Orders: nil, + }, false) if err != nil { t.Error(err) } - - err = m.AddSnapshot([]SnapshotOrder{}, tt, 1, true) + if len(m.Snapshots) != 1 { + t.Error("expected 1") + } + err = m.AddSnapshot(&Snapshot{ + Offset: 0, + Timestamp: tt, + Orders: nil, + }, true) if err != nil { t.Error(err) } + if len(m.Snapshots) != 1 { + t.Error("expected 1") + } } func TestGetSnapshotAtTime(t *testing.T) { t.Parallel() m := Manager{} tt := time.Now() - err := m.AddSnapshot([]SnapshotOrder{ - { - ClosePrice: decimal.NewFromInt(1337), + err := m.AddSnapshot(&Snapshot{Offset: 0, + Timestamp: tt, + Orders: []SnapshotOrder{ + { + Order: &gctorder.Detail{ + Price: 1337, + }, + ClosePrice: decimal.NewFromInt(1337), + }, }, - }, tt, 1, false) + }, false) if err != nil { t.Error(err) } @@ -69,22 +89,19 @@ func TestGetLatestSnapshot(t *testing.T) { t.Error("expected blank snapshot") } tt := time.Now() - err := m.AddSnapshot([]SnapshotOrder{ - { - ClosePrice: decimal.NewFromInt(1337), - }, - }, tt, 1, false) - if err != nil { - t.Error(err) - } - err = m.AddSnapshot([]SnapshotOrder{ - { - ClosePrice: decimal.NewFromInt(1337), - }, - }, tt.Add(time.Hour), 1, false) + err := m.AddSnapshot(&Snapshot{ + Offset: 0, + Timestamp: tt, + Orders: nil, + }, false) if err != nil { t.Error(err) } + err = m.AddSnapshot(&Snapshot{ + Offset: 1, + Timestamp: tt.Add(time.Hour), + Orders: nil, + }, false) snappySnap = m.GetLatestSnapshot() if snappySnap.Timestamp.Equal(tt) { t.Errorf("expected %v", tt.Add(time.Hour)) diff --git a/backtester/eventhandlers/portfolio/risk/risk.go b/backtester/eventhandlers/portfolio/risk/risk.go index 0e089d187be..36382035c53 100644 --- a/backtester/eventhandlers/portfolio/risk/risk.go +++ b/backtester/eventhandlers/portfolio/risk/risk.go @@ -37,7 +37,7 @@ func (r *Risk) EvaluateOrder(o order.Event, latestHoldings []holdings.Holding, s if ratio.GreaterThan(lookup.MaximumOrdersWithLeverageRatio) && lookup.MaximumOrdersWithLeverageRatio.GreaterThan(decimal.Zero) { return nil, fmt.Errorf("proceeding with the order would put maximum orders using leverage ratio beyond its limit of %v to %v and %w", lookup.MaximumOrdersWithLeverageRatio, ratio, errCannotPlaceLeverageOrder) } - lr := decimal.NewFromFloat(lookup.MaxLeverageRate) + lr := lookup.MaxLeverageRate if retOrder.GetLeverage().GreaterThan(lr) && lr.GreaterThan(decimal.Zero) { return nil, fmt.Errorf("proceeding with the order would put leverage rate beyond its limit of %v to %v and %w", lookup.MaxLeverageRate, retOrder.GetLeverage(), errCannotPlaceLeverageOrder) } diff --git a/backtester/eventhandlers/portfolio/risk/risk_types.go b/backtester/eventhandlers/portfolio/risk/risk_types.go index 4f673325b47..d3c1d5b92a9 100644 --- a/backtester/eventhandlers/portfolio/risk/risk_types.go +++ b/backtester/eventhandlers/portfolio/risk/risk_types.go @@ -32,6 +32,6 @@ type Risk struct { // CurrencySettings contains relevant limits to assess risk type CurrencySettings struct { MaximumOrdersWithLeverageRatio decimal.Decimal - MaxLeverageRate float64 + MaxLeverageRate decimal.Decimal MaximumHoldingRatio decimal.Decimal } From 013039c5508bf26beaef2b61892c170abc6efc49 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 17 Jan 2022 17:07:26 +1100 Subject: [PATCH 058/171] Fixing tests :tada: --- backtester/backtest/backtest.go | 4 +- backtester/backtest/backtest_test.go | 17 ++- backtester/config/config_test.go | 4 +- backtester/config/config_types.go | 8 +- ...a-api-candles-exchange-level-funding.strat | 12 +- .../dca-api-candles-multiple-currencies.strat | 10 +- ...-api-candles-simultaneous-processing.strat | 10 +- .../config/examples/dca-api-candles.strat | 8 +- .../config/examples/dca-api-trades.strat | 6 +- .../config/examples/dca-candles-live.strat | 8 +- .../config/examples/dca-csv-candles.strat | 8 +- .../config/examples/dca-csv-trades.strat | 8 +- .../examples/dca-database-candles.strat | 8 +- .../config/examples/futures-api-candles.strat | 12 +- .../config/examples/rsi-api-candles.strat | 10 +- .../t2b2-api-candles-exchange-funding.strat | 22 +-- .../eventhandlers/exchange/exchange_types.go | 2 +- .../portfolio/holdings/holdings_test.go | 42 +++--- .../eventhandlers/portfolio/portfolio.go | 6 +- .../eventhandlers/portfolio/portfolio_test.go | 108 +++++++-------- .../statistics/fundingstatistics_test.go | 8 +- .../statistics/statistics_test.go | 8 +- .../ftxquarterlyfutures.go | 79 ++++++----- backtester/eventtypes/event/event.go | 3 +- backtester/funding/funding.go | 12 +- backtester/funding/funding_test.go | 130 +++++++++--------- 26 files changed, 276 insertions(+), 277 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 2e8b9b4b38d..77259b8a7c9 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -256,7 +256,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, } if cfg.CurrencySettings[i].FuturesDetails != nil { - portSet.MaximumOrdersWithLeverageRatio = decimal.NewFromFloat(cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio) + portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate } portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet @@ -557,7 +557,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange lev = exchange.Leverage{ CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, - MaximumOrdersWithLeverageRatio: decimal.NewFromFloat(cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio), + MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, } } resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ diff --git a/backtester/backtest/backtest_test.go b/backtester/backtest/backtest_test.go index e792da624be..92cc3ca25be 100644 --- a/backtester/backtest/backtest_test.go +++ b/backtester/backtest/backtest_test.go @@ -31,10 +31,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/engine" gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) -const testExchange = "Bitstamp" +const testExchange = "ftx" var leet *decimal.Decimal @@ -472,16 +473,18 @@ func TestFullCycle(t *testing.T) { if err != nil { t.Error(err) } - _, err = port.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ex, Asset: a, Pair: cp}) + fx := &ftx.FTX{} + fx.SetDefaults() + err = port.SetupCurrencySettingsMap(&exchange.Settings{Exchange: fx, Asset: a, Pair: cp}) if err != nil { t.Error(err) } f := funding.SetupFundingManager(false, true) - b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero) + b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero, false) if err != nil { t.Error(err) } - quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero, false) if err != nil { t.Error(err) } @@ -577,16 +580,16 @@ func TestFullCycleMulti(t *testing.T) { if err != nil { t.Error(err) } - _, err = port.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ex, Asset: a, Pair: cp}) + err = port.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: a, Pair: cp}) if err != nil { t.Error(err) } f := funding.SetupFundingManager(false, true) - b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero) + b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero, false) if err != nil { t.Error(err) } - quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero, false) if err != nil { t.Error(err) } diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index d296bba6218..fa9e37b7566 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -21,7 +21,7 @@ import ( ) const ( - testExchange = "binance" + testExchange = "ftx" dca = "dollarcostaverage" // change this if you modify a config and want it to save to the example folder saveConfig = !false @@ -1228,7 +1228,7 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { CollateralCurrency: "USDT", Leverage: Leverage{ CanUseLeverage: true, - MaximumOrderLeverageRate: 1, + MaximumOrderLeverageRate: makerFee, }, }, BuySide: minMax, diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 28f2065bcca..4cd4367dc70 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -97,14 +97,14 @@ type PortfolioSettings struct { // Leverage rules are used to allow or limit the use of leverage in orders // when supported type Leverage struct { - CanUseLeverage bool `json:"can-use-leverage"` - MaximumOrdersWithLeverageRatio float64 `json:"maximum-orders-with-leverage-ratio"` + CanUseLeverage bool `json:"can-use-leverage"` + MaximumOrdersWithLeverageRatio decimal.Decimal `json:"maximum-orders-with-leverage-ratio"` // this means you can place an order with higher leverage rate. eg have $100 in collateral, // but place an order for $200 using 2x leverage - MaximumOrderLeverageRate float64 `json:"maximum-leverage-rate"` + MaximumOrderLeverageRate decimal.Decimal `json:"maximum-leverage-rate"` // this means you can place orders at `1x leverage, but utilise collateral as leverage to place more. // eg if this is 2x, and collateral is $100 I can place two long/shorts of $100 - MaximumCollateralLeverageRate float64 `json:"maximum-collateral-leverage-rate"` + MaximumCollateralLeverageRate decimal.Decimal `json:"maximum-collateral-leverage-rate"` } // MinMax are the rules which limit the placement of orders. diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index 6f0391ec142..78df483db65 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -7,7 +7,7 @@ "use-exchange-level-funding": true, "exchange-level-funding": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "currency": "USDT", "initial-funds": "100000", @@ -19,7 +19,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -43,7 +43,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "ETH", "quote": "USDT", @@ -79,9 +79,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles-multiple-currencies.strat b/backtester/config/examples/dca-api-candles-multiple-currencies.strat index 9f15b4cb3a6..073c48cbb55 100644 --- a/backtester/config/examples/dca-api-candles-multiple-currencies.strat +++ b/backtester/config/examples/dca-api-candles-multiple-currencies.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -36,7 +36,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "ETH", "quote": "USDT", @@ -75,9 +75,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat index f9588431c9c..9185ec251aa 100644 --- a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat +++ b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -36,7 +36,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "ETH", "quote": "USDT", @@ -75,9 +75,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-candles.strat b/backtester/config/examples/dca-api-candles.strat index 0ae4872666a..c6f34fa43a8 100644 --- a/backtester/config/examples/dca-api-candles.strat +++ b/backtester/config/examples/dca-api-candles.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -48,9 +48,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-api-trades.strat b/backtester/config/examples/dca-api-trades.strat index c6e8ac823ff..5790acc5e69 100644 --- a/backtester/config/examples/dca-api-trades.strat +++ b/backtester/config/examples/dca-api-trades.strat @@ -48,9 +48,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.1", diff --git a/backtester/config/examples/dca-candles-live.strat b/backtester/config/examples/dca-candles-live.strat index b6fe2ed7a3f..0bbb67e0390 100644 --- a/backtester/config/examples/dca-candles-live.strat +++ b/backtester/config/examples/dca-candles-live.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -51,9 +51,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-csv-candles.strat b/backtester/config/examples/dca-csv-candles.strat index 0ffb69bc139..b3611fd1642 100644 --- a/backtester/config/examples/dca-csv-candles.strat +++ b/backtester/config/examples/dca-csv-candles.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -46,9 +46,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/dca-csv-trades.strat b/backtester/config/examples/dca-csv-trades.strat index 7b4f43c7600..9e0e36ebeca 100644 --- a/backtester/config/examples/dca-csv-trades.strat +++ b/backtester/config/examples/dca-csv-trades.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -46,9 +46,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0", diff --git a/backtester/config/examples/dca-database-candles.strat b/backtester/config/examples/dca-database-candles.strat index 9921eac777d..0f3ee0fc1e1 100644 --- a/backtester/config/examples/dca-database-candles.strat +++ b/backtester/config/examples/dca-database-candles.strat @@ -9,7 +9,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -62,9 +62,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/futures-api-candles.strat index f3dd4d8db42..26099aee9c2 100644 --- a/backtester/config/examples/futures-api-candles.strat +++ b/backtester/config/examples/futures-api-candles.strat @@ -26,9 +26,9 @@ "futures-details": { "leverage": { "can-use-leverage": true, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 1, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0.001", + "maximum-collateral-leverage-rate": "0" }, "collateral-currency": "USDT" }, @@ -64,9 +64,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": true, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/rsi-api-candles.strat b/backtester/config/examples/rsi-api-candles.strat index 60978657860..ad0069445b7 100644 --- a/backtester/config/examples/rsi-api-candles.strat +++ b/backtester/config/examples/rsi-api-candles.strat @@ -14,7 +14,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -41,7 +41,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "ETH", "quote": "USDT", @@ -81,9 +81,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 662e6853f6b..1d633afbee2 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -7,7 +7,7 @@ "use-exchange-level-funding": true, "exchange-level-funding": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "currency": "BTC", "initial-funds": "3", @@ -15,7 +15,7 @@ "collateral": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "currency": "USDT", "initial-funds": "10000", @@ -32,7 +32,7 @@ }, "currency-settings": [ { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BTC", "quote": "USDT", @@ -56,7 +56,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "DOGE", "quote": "USDT", @@ -80,7 +80,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "ETH", "quote": "BTC", @@ -104,7 +104,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "LTC", "quote": "BTC", @@ -128,7 +128,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "XRP", "quote": "USDT", @@ -152,7 +152,7 @@ "use-exchange-pnl-calculation": false }, { - "exchange-name": "binance", + "exchange-name": "ftx", "asset": "spot", "base": "BNB", "quote": "BTC", @@ -188,9 +188,9 @@ "portfolio-settings": { "leverage": { "can-use-leverage": false, - "maximum-orders-with-leverage-ratio": 0, - "maximum-leverage-rate": 0, - "maximum-collateral-leverage-rate": 0 + "maximum-orders-with-leverage-ratio": "0", + "maximum-leverage-rate": "0", + "maximum-collateral-leverage-rate": "0" }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index f9df249ae97..cbb9762c2f6 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -74,5 +74,5 @@ type MinMax struct { type Leverage struct { CanUseLeverage bool MaximumOrdersWithLeverageRatio decimal.Decimal - MaximumLeverageRate float64 + MaximumLeverageRate decimal.Decimal } diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index bc6797a91b1..7cec732c5f6 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -21,17 +21,13 @@ const ( testExchange = "binance" ) -var ( - riskFreeRate = decimal.NewFromFloat(0.03) -) - func pair(t *testing.T) *funding.Pair { t.Helper() - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero, false) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero, false) if err != nil { t.Fatal(err) } @@ -48,7 +44,9 @@ func TestCreate(t *testing.T) { if !errors.Is(err, common.ErrNilEvent) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } - _, err = Create(&fill.Fill{}, pair(t)) + _, err = Create(&fill.Fill{ + Base: event.Base{AssetType: asset.Spot}, + }, pair(t)) if err != nil { t.Error(err) } @@ -56,7 +54,9 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { t.Parallel() - h, err := Create(&fill.Fill{}, pair(t)) + h, err := Create(&fill.Fill{ + Base: event.Base{AssetType: asset.Spot}, + }, pair(t)) if err != nil { t.Error(err) } @@ -74,7 +74,9 @@ func TestUpdate(t *testing.T) { func TestUpdateValue(t *testing.T) { t.Parallel() - h, err := Create(&fill.Fill{}, pair(t)) + h, err := Create(&fill.Fill{ + Base: event.Base{AssetType: asset.Spot}, + }, pair(t)) if err != nil { t.Error(err) } @@ -89,11 +91,11 @@ func TestUpdateValue(t *testing.T) { func TestUpdateBuyStats(t *testing.T) { t.Parallel() - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) if err != nil { t.Fatal(err) } @@ -101,7 +103,9 @@ func TestUpdateBuyStats(t *testing.T) { if err != nil { t.Fatal(err) } - h, err := Create(&fill.Fill{}, p) + h, err := Create(&fill.Fill{ + Base: event.Base{AssetType: asset.Spot}, + }, pair(t)) if err != nil { t.Error(err) } @@ -218,11 +222,11 @@ func TestUpdateBuyStats(t *testing.T) { func TestUpdateSellStats(t *testing.T) { t.Parallel() - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) if err != nil { t.Fatal(err) } @@ -230,7 +234,10 @@ func TestUpdateSellStats(t *testing.T) { if err != nil { t.Fatal(err) } - h, err := Create(&fill.Fill{}, p) + + h, err := Create(&fill.Fill{ + Base: event.Base{AssetType: asset.Spot}, + }, p) if err != nil { t.Error(err) } @@ -247,13 +254,11 @@ func TestUpdateSellStats(t *testing.T) { ClosePrice: decimal.NewFromInt(500), VolumeAdjustedPrice: decimal.NewFromInt(500), PurchasePrice: decimal.NewFromInt(500), - ExchangeFee: decimal.Zero, - Slippage: decimal.Zero, Order: &order.Detail{ Price: 500, Amount: 1, Exchange: testExchange, - ID: "decimal.NewFromInt(1337)", + ID: "1337", Type: order.Limit, Side: order.Buy, Status: order.New, @@ -262,7 +267,6 @@ func TestUpdateSellStats(t *testing.T) { CloseTime: time.Now(), LastUpdated: time.Now(), Pair: currency.NewPair(currency.BTC, currency.USDT), - Trades: nil, Fee: 1, }, }, p) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 1f0a248c128..0d101ecff4b 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -76,6 +76,9 @@ func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings) error return nil } + if !settings.Asset.IsFutures() { + return nil + } futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ Exchange: name, Asset: settings.Asset, @@ -551,7 +554,7 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // that are not closed and calculate their PNL func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { if !e.GetAssetType().IsFutures() { - return nil // or err + return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFutureAsset) } settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] if !ok { @@ -574,6 +577,7 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { return err } log.Infof(log.BackTester, "%+v", pnl) + return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 7e3f5489e48..9c738862a09 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -2,6 +2,7 @@ package portfolio import ( "errors" + "strings" "testing" "time" @@ -684,24 +685,19 @@ func TestGetLatestSnapshot(t *testing.T) { } func TestCalculatePNL(t *testing.T) { - p := &Portfolio{ - riskFreeRate: decimal.Decimal{}, - sizeManager: nil, - riskManager: nil, - exchangeAssetPairSettings: nil, - } - + p := &Portfolio{} ev := &kline.Kline{} err := p.CalculatePNL(ev) - if !errors.Is(err, errNoPortfolioSettings) { - t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) + if !errors.Is(err, gctorder.ErrNotFutureAsset) { + t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFutureAsset) } - exch := "binance" + exch := &ftx.FTX{} + exch.SetDefaults() a := asset.Futures pair, _ := currency.NewPairFromStrings("BTC", "1231") err = p.SetupCurrencySettingsMap(&exchange.Settings{ - Exchange: &ftx.FTX{}, + Exchange: exch, UseRealOrders: false, Pair: pair, Asset: a, @@ -709,9 +705,9 @@ func TestCalculatePNL(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } - tt := time.Now() + tt := time.Now().Add(time.Hour) tt0 := time.Now().Add(-time.Hour) - ev.Exchange = exch + ev.Exchange = exch.Name ev.AssetType = a ev.CurrencyPair = pair ev.Time = tt0 @@ -721,64 +717,60 @@ func TestCalculatePNL(t *testing.T) { t.Errorf("received: %v, expected: %v", err, nil) } - futuresOrder := &gctorder.PositionTracker{ - CurrentDirection: gctorder.Short, - ShortPositions: &gctorder.Detail{ - Price: 1336, - Amount: 20, - Exchange: exch, - Side: gctorder.Short, - AssetType: asset.Futures, - Date: tt0, - Pair: pair, - }, + od := &gctorder.Detail{ + Price: 1336, + Amount: 20, + Exchange: exch.Name, + Side: gctorder.Short, + AssetType: a, + Date: tt0, + Pair: pair, + ID: "lol", } - s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][pair] + + s, ok := p.exchangeAssetPairSettings[strings.ToLower(exch.Name)][a][pair] if !ok { t.Fatal("couldn't get settings") } ev.Close = decimal.NewFromInt(1337) err = s.ComplianceManager.AddSnapshot(&compliance.Snapshot{ Offset: 0, - Timestamp: time.Time{}, - Orders: nil, + Timestamp: tt0, + Orders: []compliance.SnapshotOrder{ + { + Order: od, + }, + }, }, false) - err = p.CalculatePNL(ev, nil) + odCp := od.Copy() + odCp.Price = od.Price - 1 + odCp.Side = gctorder.Long + err = s.ComplianceManager.AddSnapshot(&compliance.Snapshot{ + Offset: 1, + Timestamp: tt, + Orders: []compliance.SnapshotOrder{ + { + Order: od, + }, + { + Order: &odCp, + }, + }, + }, false) + err = p.CalculatePNL(ev) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } - if len(futuresOrder.PNLHistory) == 0 { - t.Error("expected a pnl entry ( ͡° ͜ʖ ͡°)") + pos := s.FuturesTracker.GetPositions() + if len(pos) != 1 { + t.Fatalf("expected one position, received '%v'", len(pos)) } - - if !futuresOrder.UnrealisedPNL.Equal(decimal.NewFromInt(20)) { - // 20 orders * $1 difference * 1x leverage - t.Error("expected 20") + if len(pos[0].GetStats().PNLHistory) == 0 { + t.Fatal("expected a pnl entry ( ͡° ͜ʖ ͡°)") } - - err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ - { - ClosePrice: decimal.NewFromInt(1336), - Order: futuresOrder.ShortPositions, - }, - }, tt, 1, false) - err = p.CalculatePNL(ev, nil) - if !errors.Is(err, nil) { - t.Errorf("received: %v, expected: %v", err, nil) - } - - // coverage of logic - futuresOrder.LongPositions = futuresOrder.ShortPositions - - err = s.ComplianceManager.AddSnapshot([]compliance.SnapshotOrder{ - { - ClosePrice: decimal.NewFromInt(1336), - FuturesOrder: futuresOrder, - }, - }, tt.Add(time.Hour), 2, false) - err = p.CalculatePNL(ev, nil) - if !errors.Is(err, nil) { - t.Errorf("received: %v, expected: %v", err, nil) + if !pos[0].GetStats().RealisedPNL.Equal(decimal.NewFromInt(20)) { + // 20 orders * $1 difference * 1x leverage + t.Errorf("expected 20, received '%v'", pos[0].GetStats().RealisedPNL) } } diff --git a/backtester/eventhandlers/statistics/fundingstatistics_test.go b/backtester/eventhandlers/statistics/fundingstatistics_test.go index adb009dfc83..c517d79b5be 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics_test.go +++ b/backtester/eventhandlers/statistics/fundingstatistics_test.go @@ -21,7 +21,7 @@ func TestCalculateFundingStatistics(t *testing.T) { t.Errorf("received %v expected %v", err, common.ErrNilArguments) } f := funding.SetupFundingManager(true, true) - item, err := funding.CreateItem("binance", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) + item, err := funding.CreateItem("binance", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } @@ -30,7 +30,7 @@ func TestCalculateFundingStatistics(t *testing.T) { t.Errorf("received %v expected %v", err, nil) } - item2, err := funding.CreateItem("binance", asset.Spot, currency.USD, decimal.NewFromInt(1337), decimal.Zero) + item2, err := funding.CreateItem("binance", asset.Spot, currency.USD, decimal.NewFromInt(1337), decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } @@ -184,11 +184,11 @@ func TestFundingStatisticsPrintResults(t *testing.T) { } funds := funding.SetupFundingManager(true, true) - item1, err := funding.CreateItem("test", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04)) + item1, err := funding.CreateItem("test", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04), false) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } - item2, err := funding.CreateItem("test", asset.Spot, currency.LTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04)) + item2, err := funding.CreateItem("test", asset.Spot, currency.LTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04), false) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 165b6d9574b..7c2b9eedc0d 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -738,11 +738,11 @@ func TestCalculateTheResults(t *testing.T) { s.ExchangeAssetPairStatistics[exch][a][p2].Events[1].Holdings.TotalValue = eleeet funds := funding.SetupFundingManager(false, false) - pBase, err := funding.CreateItem(exch, a, p.Base, eleeet, decimal.Zero) + pBase, err := funding.CreateItem(exch, a, p.Base, eleeet, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pQuote, err := funding.CreateItem(exch, a, p.Quote, eleeet, decimal.Zero) + pQuote, err := funding.CreateItem(exch, a, p.Quote, eleeet, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -755,11 +755,11 @@ func TestCalculateTheResults(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pBase2, err := funding.CreateItem(exch, a, p2.Base, eleeet, decimal.Zero) + pBase2, err := funding.CreateItem(exch, a, p2.Base, eleeet, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pQuote2, err := funding.CreateItem(exch, a, p2.Quote, eleeet, decimal.Zero) + pQuote2, err := funding.CreateItem(exch, a, p2.Quote, eleeet, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go index 7c259facf8d..cb030db1d49 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go @@ -15,7 +15,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/funding" gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) const ( @@ -93,51 +92,51 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions. RSI %v", d.Latest().GetTime(), latestRSIValue)) return &es, nil } + /* - currentOrders, err := p.GetLatestOrderSnapshotForEvent(&es) - if err != nil { - return nil, err - } - - var o *order.PositionTracker - for i := range currentOrders.Orders { - if currentOrders.Orders[i].FuturesOrder != nil { - if currentOrders.Orders[i].FuturesOrder.LongPositions == nil { - if currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Short || currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Long { - o = currentOrders.Orders[i].FuturesOrder - } - } + currentOrders, err := p.GetLatestOrderSnapshotForEvent(&es) + if err != nil { + return nil, err } - } - if o.ShortPositions == nil { - switch { - case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): - es.SetDirection(order.Short) - case latestRSIValue.LessThanOrEqual(s.rsiLow): - es.SetDirection(order.Long) - default: - es.SetDirection(common.DoNothing) + var o *order.PositionTracker + for i := range currentOrders.Orders { + //if currentOrders.Orders[i].FuturesOrder != nil { + // if currentOrders.Orders[i].FuturesOrder.LongPositions == nil { + // if currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Short || currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Long { + // o = currentOrders.Orders[i].FuturesOrder + // } + // } + //} } - es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) - } else { - price := decimal.NewFromFloat(o.ShortPositions.Price) - if latestRSIValue.LessThanOrEqual(s.rsiLow) || - latestRSIValue.GreaterThanOrEqual(s.rsiHigh) || - (!s.stopLoss.IsZero() && latest.GetClosePrice().LessThanOrEqual(s.stopLoss)) || - (!s.takeProfit.IsZero() && latest.GetClosePrice().GreaterThanOrEqual(s.takeProfit)) || - (!s.trailingStop.IsZero() && latest.GetClosePrice().Sub(price).Div(price).Mul(decimal.NewFromInt(100)).LessThanOrEqual(s.trailingStop)) || - o.ShortPositions.UnrealisedPNL.GreaterThanOrEqual(s.highestUnrealised) || - o.ShortPositions.UnrealisedPNL.LessThanOrEqual(s.lowestUnrealised) { - // set up the counter order to close the position - es.SetAmount(decimal.NewFromFloat(o.ShortPositions.Amount)) - if o.ShortPositions.Side == order.Short { - es.SetDirection(order.Long) - } else if o.ShortPositions.Side == order.Long { + if o.ShortPositions == nil { + switch { + case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): es.SetDirection(order.Short) + case latestRSIValue.LessThanOrEqual(s.rsiLow): + es.SetDirection(order.Long) + default: + es.SetDirection(common.DoNothing) + } + es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) + } else { + price := decimal.NewFromFloat(o.ShortPositions.Price) + if latestRSIValue.LessThanOrEqual(s.rsiLow) || + latestRSIValue.GreaterThanOrEqual(s.rsiHigh) || + (!s.stopLoss.IsZero() && latest.GetClosePrice().LessThanOrEqual(s.stopLoss)) || + (!s.takeProfit.IsZero() && latest.GetClosePrice().GreaterThanOrEqual(s.takeProfit)) || + (!s.trailingStop.IsZero() && latest.GetClosePrice().Sub(price).Div(price).Mul(decimal.NewFromInt(100)).LessThanOrEqual(s.trailingStop)) || + o.ShortPositions.UnrealisedPNL.GreaterThanOrEqual(s.highestUnrealised) || + o.ShortPositions.UnrealisedPNL.LessThanOrEqual(s.lowestUnrealised) { + // set up the counter order to close the position + es.SetAmount(decimal.NewFromFloat(o.ShortPositions.Amount)) + if o.ShortPositions.Side == order.Short { + es.SetDirection(order.Long) + } else if o.ShortPositions.Side == order.Long { + es.SetDirection(order.Short) + } } } - } - + */ return &es, nil } diff --git a/backtester/eventtypes/event/event.go b/backtester/eventtypes/event/event.go index 0d88d38c510..ad2660af98b 100644 --- a/backtester/eventtypes/event/event.go +++ b/backtester/eventtypes/event/event.go @@ -1,6 +1,7 @@ package event import ( + "strings" "time" "github.com/thrasher-corp/gocryptotrader/currency" @@ -35,7 +36,7 @@ func (b *Base) Pair() currency.Pair { // GetExchange returns the exchange func (b *Base) GetExchange() string { - return b.Exchange + return strings.ToLower(b.Exchange) } // GetAssetType returns the asset type diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index f77ac0eef82..0a2d94121d2 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -62,7 +62,7 @@ func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, trans } return &Item{ - exchange: exch, + exchange: strings.ToLower(exch), asset: a, currency: ci, initialFunds: initialFunds, @@ -361,16 +361,16 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { + var resp Pair + var collat Collateral for i := range f.items { if a.IsFutures() { - var resp Collateral if f.items[i].BasicEqual(exch, a, currency.NewCode(p.String()), currency.USDT) { - resp.Contract = f.items[i] - resp.Collateral = f.items[i].pairedWith - return &resp, nil + collat.Contract = f.items[i] + collat.Collateral = f.items[i].pairedWith + return &collat, nil } } else { - var resp Pair if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { resp.Base = f.items[i] continue diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 38089b65e7e..0b627aa5455 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -46,7 +46,7 @@ func TestSetupFundingManager(t *testing.T) { func TestReset(t *testing.T) { t.Parallel() f := SetupFundingManager(true, false) - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -136,7 +136,7 @@ func TestAddItem(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -158,7 +158,7 @@ func TestExists(t *testing.T) { if exists { t.Errorf("received '%v' expected '%v'", exists, false) } - conflictingSingleItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + conflictingSingleItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -170,11 +170,11 @@ func TestExists(t *testing.T) { if !exists { t.Errorf("received '%v' expected '%v'", exists, true) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -186,27 +186,45 @@ func TestExists(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pairItems, err := f.GetFundingForEAP(exch, a, pair) + _, err = f.GetFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - exists = f.Exists(pairItems.Base) - if !exists { - t.Errorf("received '%v' expected '%v'", exists, true) - } - exists = f.Exists(pairItems.Quote) - if !exists { - t.Errorf("received '%v' expected '%v'", exists, true) - } - funds, err := f.GetFundingForEAP(exch, a, pair) + _, err = f.GetFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } // demonstration that you don't need the original *Items // to check for existence, just matching fields - baseCopy := *funds.Base - quoteCopy := *funds.Quote + baseCopy := Item{ + exchange: baseItem.exchange, + asset: baseItem.asset, + currency: baseItem.currency, + initialFunds: baseItem.initialFunds, + available: baseItem.available, + reserved: baseItem.reserved, + transferFee: baseItem.transferFee, + pairedWith: baseItem.pairedWith, + usdTrackingCandles: baseItem.usdTrackingCandles, + snapshot: baseItem.snapshot, + collateral: baseItem.collateral, + collateralCandles: baseItem.collateralCandles, + } + quoteCopy := Item{ + exchange: quoteItem.exchange, + asset: quoteItem.asset, + currency: quoteItem.currency, + initialFunds: quoteItem.initialFunds, + available: quoteItem.available, + reserved: quoteItem.reserved, + transferFee: quoteItem.transferFee, + pairedWith: quoteItem.pairedWith, + usdTrackingCandles: quoteItem.usdTrackingCandles, + snapshot: quoteItem.snapshot, + collateral: quoteItem.collateral, + collateralCandles: quoteItem.collateralCandles, + } quoteCopy.pairedWith = &baseCopy exists = f.Exists(&baseCopy) if !exists { @@ -225,11 +243,11 @@ func TestExists(t *testing.T) { func TestAddPair(t *testing.T) { t.Parallel() f := FundManager{} - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -241,32 +259,10 @@ func TestAddPair(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - resp, err := f.GetFundingForEAP(exch, a, pair) + _, err = f.GetFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - if resp.Base.exchange != exch || - resp.Base.asset != a || - resp.Base.currency != pair.Base { - t.Error("woah nelly") - } - if resp.Quote.exchange != exch || - resp.Quote.asset != a || - resp.Quote.currency != pair.Quote { - t.Error("woah nelly") - } - if resp.Quote.pairedWith != resp.Base { - t.Errorf("received '%v' expected '%v'", resp.Base, resp.Quote.pairedWith) - } - if resp.Base.pairedWith != resp.Quote { - t.Errorf("received '%v' expected '%v'", resp.Quote, resp.Base.pairedWith) - } - if !resp.Base.initialFunds.Equal(decimal.Zero) { - t.Errorf("received '%v' expected '%v'", resp.Base.initialFunds, decimal.Zero) - } - if !resp.Quote.initialFunds.Equal(elite) { - t.Errorf("received '%v' expected '%v'", resp.Quote.initialFunds, elite) - } p, err = CreatePair(baseItem, quoteItem) if !errors.Is(err, nil) { @@ -301,11 +297,11 @@ func TestGetFundingForEvent(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -330,7 +326,7 @@ func TestGetFundingForEAC(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -356,11 +352,11 @@ func TestGetFundingForEAP(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -397,11 +393,11 @@ func TestGetFundingForEAP(t *testing.T) { func TestBaseInitialFunds(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -416,11 +412,11 @@ func TestBaseInitialFunds(t *testing.T) { func TestQuoteInitialFunds(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -435,11 +431,11 @@ func TestQuoteInitialFunds(t *testing.T) { func TestBaseAvailable(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -454,11 +450,11 @@ func TestBaseAvailable(t *testing.T) { func TestQuoteAvailable(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -473,11 +469,11 @@ func TestQuoteAvailable(t *testing.T) { func TestReservePair(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -508,11 +504,11 @@ func TestReservePair(t *testing.T) { func TestReleasePair(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -566,11 +562,11 @@ func TestReleasePair(t *testing.T) { func TestIncreaseAvailablePair(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -715,11 +711,11 @@ func TestMatchesItemCurrency(t *testing.T) { if i.MatchesItemCurrency(nil) { t.Errorf("received '%v' expected '%v'", true, false) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -737,11 +733,11 @@ func TestMatchesExchange(t *testing.T) { if i.MatchesExchange(nil) { t.Errorf("received '%v' expected '%v'", true, false) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -916,7 +912,7 @@ func TestAddUSDTrackingData(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -959,7 +955,7 @@ func TestAddUSDTrackingData(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - usdtItem, err := CreateItem(exch, a, currency.USDT, elite, decimal.Zero) + usdtItem, err := CreateItem(exch, a, currency.USDT, elite, decimal.Zero, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } From 3b3369ab1bfb6ad8fd9467c5b70e922efac1029e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 18 Jan 2022 14:24:54 +1100 Subject: [PATCH 059/171] Fixes all the tests --- backtester/backtest/backtest.go | 8 ++ backtester/backtest/backtest_test.go | 8 +- backtester/eventhandlers/exchange/exchange.go | 6 +- .../eventhandlers/exchange/exchange_test.go | 54 ++++++---- .../eventhandlers/exchange/exchange_types.go | 9 +- .../eventhandlers/portfolio/portfolio.go | 74 ++++++------- .../eventhandlers/portfolio/portfolio_test.go | 100 +++++++++++------- .../portfolio/portfolio_types.go | 7 -- .../ftxquarterlyfutures.go | 7 +- .../ftxquarterlyfutures_test.go | 21 ++-- backtester/funding/collateral.go | 1 + backtester/funding/item.go | 2 +- backtester/funding/pair.go | 3 + 13 files changed, 169 insertions(+), 131 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 77259b8a7c9..cf1372f4f3a 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -165,6 +165,11 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, return nil, err } conf.Enabled = true + // this is required because fuck you + conf.WebsocketTrafficTimeout = time.Second + conf.Websocket = convert.BoolPtr(false) + conf.WebsocketResponseCheckTimeout = time.Second + conf.WebsocketResponseMaxLimit = time.Second err = exch.Setup(conf) if err != nil { return nil, err @@ -225,6 +230,9 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, return nil, err } exchBase := exch.GetBase() + exchBase.Config.WebsocketTrafficTimeout = time.Second + exchBase.WebsocketResponseCheckTimeout = time.Second + var requestFormat currency.PairFormat requestFormat, err = exchBase.GetPairFormat(a, true) if err != nil { diff --git a/backtester/backtest/backtest_test.go b/backtester/backtest/backtest_test.go index 92cc3ca25be..db675e90470 100644 --- a/backtester/backtest/backtest_test.go +++ b/backtester/backtest/backtest_test.go @@ -76,12 +76,6 @@ func TestNewFromConfig(t *testing.T) { } cfg.CurrencySettings[0].Asset = asset.Spot.String() _, err = NewFromConfig(cfg, "", "") - if !errors.Is(err, currency.ErrPairNotFound) { - t.Errorf("received: %v, expected: %v", err, currency.ErrPairNotFound) - } - - cfg.CurrencySettings[0].Base = "btc" - cfg.CurrencySettings[0].Quote = "usd" _, err = NewFromConfig(cfg, "", "") if !errors.Is(err, base.ErrStrategyNotFound) { t.Errorf("received: %v, expected: %v", err, base.ErrStrategyNotFound) @@ -474,7 +468,7 @@ func TestFullCycle(t *testing.T) { t.Error(err) } fx := &ftx.FTX{} - fx.SetDefaults() + fx.Name = testExchange err = port.SetupCurrencySettingsMap(&exchange.Settings{Exchange: fx, Asset: a, Pair: cp}) if err != nil { t.Error(err) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 8cd1b81304d..9fd3eeef4a4 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -116,7 +116,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * case asset.Spot: pr, fundErr := funds.GetPairReleaser() if fundErr != nil { - return f, err + return f, fundErr } if err != nil { fundErr = pr.Release(eventFunds, eventFunds, f.GetDirection()) @@ -134,7 +134,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * case gctorder.Buy: fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) if fundErr != nil { - return f, err + return f, fundErr } pr.IncreaseAvailable(limitReducedAmount, f.GetDirection()) case gctorder.Sell: @@ -369,7 +369,7 @@ func (e *Exchange) GetCurrencySettings(exch string, a asset.Item, cp currency.Pa } } } - return Settings{}, fmt.Errorf("no currency settings found for %v %v %v", exch, a, cp) + return Settings{}, fmt.Errorf("%w for %v %v %v", errNoCurrencySettingsFound, exch, a, cp) } func ensureOrderFitsWithinHLV(slippagePrice, amount, high, low, volume decimal.Decimal) (adjustedPrice, adjustedAmount decimal.Decimal) { diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index f31c157583f..96369050ce7 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -36,7 +36,27 @@ func (f *fakeFund) GetCollateralReader() (funding.ICollateralReader, error) { } func (f *fakeFund) GetPairReleaser() (funding.IPairReleaser, error) { - return nil, nil + btc, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(9999), decimal.NewFromInt(9999), false) + if err != nil { + return nil, err + } + usd, err := funding.CreateItem(testExchange, asset.Spot, currency.USD, decimal.NewFromInt(9999), decimal.NewFromInt(9999), false) + if err != nil { + return nil, err + } + p, err := funding.CreatePair(btc, usd) + if err != nil { + return nil, err + } + err = p.Reserve(decimal.NewFromInt(1337), gctorder.Buy) + if err != nil { + return nil, err + } + err = p.Reserve(decimal.NewFromInt(1337), gctorder.Sell) + if err != nil { + return nil, err + } + return p, nil } func (f *fakeFund) GetCollateralReleaser() (funding.ICollateralReleaser, error) { return nil, nil @@ -65,8 +85,10 @@ func TestSetCurrency(t *testing.T) { if len(e.CurrencySettings) != 0 { t.Error("expected 0") } + f := &ftx.FTX{} + f.Name = testExchange cs := &Settings{ - Exchange: &ftx.FTX{}, + Exchange: f, UseRealOrders: true, Pair: currency.NewPair(currency.BTC, currency.USDT), Asset: asset.Spot, @@ -188,7 +210,7 @@ func TestPlaceOrder(t *testing.T) { } f := &fill.Fill{} _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, f, bot.OrderManager) - if err != nil && err.Error() != "order exchange name must be specified" { + if !errors.Is(err, engine.ErrExchangeNameIsEmpty) { t.Error(err) } @@ -246,24 +268,19 @@ func TestExecuteOrder(t *testing.T) { if err != nil { t.Fatal(err) } - + f := &ftx.FTX{} + f.Name = testExchange cs := Settings{ - Exchange: &ftx.FTX{}, + Exchange: f, UseRealOrders: false, Pair: p, Asset: a, ExchangeFee: decimal.NewFromFloat(0.01), MakerFee: decimal.NewFromFloat(0.01), TakerFee: decimal.NewFromFloat(0.01), - BuySide: MinMax{}, - SellSide: MinMax{}, - Leverage: Leverage{}, - MinimumSlippageRate: decimal.Zero, MaximumSlippageRate: decimal.NewFromInt(1), } - e := Exchange{ - CurrencySettings: []Settings{cs}, - } + e := Exchange{} ev := event.Base{ Exchange: testExchange, Time: time.Now(), @@ -280,9 +297,9 @@ func TestExecuteOrder(t *testing.T) { d := &kline.DataFromKline{ Item: gctkline.Item{ - Exchange: "", - Pair: currency.Pair{}, - Asset: "", + Exchange: testExchange, + Pair: p, + Asset: a, Interval: 0, Candles: []gctkline.Candle{ { @@ -300,7 +317,7 @@ func TestExecuteOrder(t *testing.T) { } d.Next() _, err = e.ExecuteOrder(o, d, bot.OrderManager, &fakeFund{}) - if err != errNilCurrencySettings { + if !errors.Is(err, errNoCurrencySettingsFound) { t.Error(err) } @@ -359,9 +376,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { if err != nil { t.Fatal(err) } - + f := &ftx.FTX{} + f.Name = testExchange cs := Settings{ - Exchange: &ftx.FTX{}, + Exchange: f, UseRealOrders: false, Pair: p, Asset: a, diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index cbb9762c2f6..55ba93fe1ca 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -16,10 +16,11 @@ import ( ) var ( - errDataMayBeIncorrect = errors.New("data may be incorrect") - errExceededPortfolioLimit = errors.New("exceeded portfolio limit") - errNilCurrencySettings = errors.New("received nil currency settings") - errInvalidDirection = errors.New("received invalid order direction") + errDataMayBeIncorrect = errors.New("data may be incorrect") + errExceededPortfolioLimit = errors.New("exceeded portfolio limit") + errNilCurrencySettings = errors.New("received nil currency settings") + errInvalidDirection = errors.New("received invalid order direction") + errNoCurrencySettingsFound = errors.New("no currency settings found") ) // ExecutionHandler interface dictates what functions are required to submit an order diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 0d101ecff4b..5e6d51a54ec 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -49,62 +49,62 @@ func (p *Portfolio) Reset() { } // SetupCurrencySettingsMap ensures a map is created and no panics happen -func (p *Portfolio) SetupCurrencySettingsMap(settings *exchange.Settings) error { - if settings == nil { +func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { + if setup == nil { return errNoPortfolioSettings } - if settings.Exchange == nil { + if setup.Exchange == nil { return errExchangeUnset } - if settings.Asset == "" { + if setup.Asset == "" { return errAssetUnset } - if settings.Pair.IsEmpty() { + if setup.Pair.IsEmpty() { return errCurrencyPairUnset } if p.exchangeAssetPairSettings == nil { p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) } - name := strings.ToLower(settings.Exchange.GetName()) + name := strings.ToLower(setup.Exchange.GetName()) if p.exchangeAssetPairSettings[name] == nil { p.exchangeAssetPairSettings[name] = make(map[asset.Item]map[currency.Pair]*Settings) } - if p.exchangeAssetPairSettings[name][settings.Asset] == nil { - p.exchangeAssetPairSettings[name][settings.Asset] = make(map[currency.Pair]*Settings) + if p.exchangeAssetPairSettings[name][setup.Asset] == nil { + p.exchangeAssetPairSettings[name][setup.Asset] = make(map[currency.Pair]*Settings) } - if _, ok := p.exchangeAssetPairSettings[name][settings.Asset][settings.Pair]; ok { + if _, ok := p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair]; ok { return nil } - if !settings.Asset.IsFutures() { - return nil - } - futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ - Exchange: name, - Asset: settings.Asset, - Pair: settings.Pair, - Underlying: settings.Pair.Base, - OfflineCalculation: true, - UseExchangePNLCalculation: settings.UseExchangePNLCalculation, - } - if settings.UseExchangePNLCalculation { - futureTrackerSetup.ExchangePNLCalculation = settings.Exchange - } - tracker, err := gctorder.SetupMultiPositionTracker(futureTrackerSetup) - if err != nil { - return err - } - p.exchangeAssetPairSettings[name][settings.Asset][settings.Pair] = &Settings{ - Fee: settings.ExchangeFee, - BuySideSizing: settings.BuySide, - SellSideSizing: settings.SellSide, - Leverage: settings.Leverage, + settings := &Settings{ + Fee: setup.ExchangeFee, + BuySideSizing: setup.BuySide, + SellSideSizing: setup.SellSide, + Leverage: setup.Leverage, ComplianceManager: compliance.Manager{ Snapshots: []compliance.Snapshot{}, }, - Exchange: settings.Exchange, - FuturesTracker: tracker, + Exchange: setup.Exchange, + } + if setup.Asset.IsFutures() { + futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ + Exchange: name, + Asset: setup.Asset, + Pair: setup.Pair, + Underlying: setup.Pair.Base, + OfflineCalculation: true, + UseExchangePNLCalculation: setup.UseExchangePNLCalculation, + } + if setup.UseExchangePNLCalculation { + futureTrackerSetup.ExchangePNLCalculation = setup.Exchange + } + tracker, err := gctorder.SetupMultiPositionTracker(futureTrackerSetup) + if err != nil { + return err + } + settings.FuturesTracker = tracker } + p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair] = settings return nil } @@ -414,12 +414,6 @@ func (p *Portfolio) GetComplianceManager(exchangeName string, a asset.Item, cp c return &lookup.ComplianceManager, nil } -// SetFee sets the fee rate -func (p *Portfolio) SetFee(exch string, a asset.Item, cp currency.Pair, fee decimal.Decimal) { - lookup := p.exchangeAssetPairSettings[exch][a][cp] - lookup.Fee = fee -} - // GetFee can panic for bad requests, but why are you getting things that don't exist? func (p *Portfolio) GetFee(exchangeName string, a asset.Item, cp currency.Pair) decimal.Decimal { if p.exchangeAssetPairSettings == nil { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 9c738862a09..d5566fe0df5 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -26,7 +26,7 @@ import ( gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) -const testExchange = "binance" +const testExchange = "ftx" func TestReset(t *testing.T) { t.Parallel() @@ -78,17 +78,19 @@ func TestSetupCurrencySettingsMap(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errExchangeUnset) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff}) if !errors.Is(err, errAssetUnset) { t.Errorf("received: %v, expected: %v", err, errAssetUnset) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot}) if !errors.Is(err, errCurrencyPairUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyPairUnset) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -109,7 +111,9 @@ func TestSetHoldings(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -149,7 +153,9 @@ func TestGetLatestHoldingsForAllCurrencies(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -211,7 +217,9 @@ func TestViewHoldingAtTimePeriod(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoHoldings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -284,7 +292,9 @@ func TestUpdate(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -309,14 +319,16 @@ func TestGetFee(t *testing.T) { if !f.IsZero() { t.Error("expected 0") } - - err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + cp := currency.NewPair(currency.BTC, currency.USD) + ff := &ftx.FTX{} + ff.Name = testExchange + err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: cp}) if err != nil { t.Error(err) } - p.SetFee("hi", asset.Spot, currency.NewPair(currency.BTC, currency.USD), decimal.NewFromInt(1337)) - f = p.GetFee("hi", asset.Spot, currency.NewPair(currency.BTC, currency.USD)) + p.exchangeAssetPairSettings[testExchange][asset.Spot][cp].Fee = decimal.NewFromInt(1337) + f = p.GetFee(testExchange, asset.Spot, cp) if !f.Equal(decimal.NewFromInt(1337)) { t.Errorf("expected %v received %v", 1337, f) } @@ -330,12 +342,14 @@ func TestGetComplianceManager(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } var cm *compliance.Manager - cm, err = p.GetComplianceManager("hi", asset.Spot, currency.NewPair(currency.BTC, currency.USD)) + cm, err = p.GetComplianceManager(testExchange, asset.Spot, currency.NewPair(currency.BTC, currency.USD)) if err != nil { t.Error(err) } @@ -357,19 +371,21 @@ func TestAddComplianceSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } err = p.addComplianceSnapshot(&fill.Fill{ Base: event.Base{ - Exchange: "hi", + Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, }, Order: &gctorder.Detail{ - Exchange: "hi", + Exchange: testExchange, Pair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, }, @@ -389,12 +405,12 @@ func TestOnFill(t *testing.T) { f := &fill.Fill{ Base: event.Base{ - Exchange: "hi", + Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, }, Order: &gctorder.Detail{ - Exchange: "hi", + Exchange: testExchange, Pair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, }, @@ -403,7 +419,9 @@ func TestOnFill(t *testing.T) { if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -480,13 +498,15 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } s = &signal.Signal{ Base: event.Base{ - Exchange: "hi", + Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, }, @@ -518,7 +538,7 @@ func TestOnSignal(t *testing.T) { s.Direction = gctorder.Buy err = p.setHoldingsForOffset(&holdings.Holding{ - Exchange: testExchange, + Exchange: "lol", Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD), Timestamp: time.Now(), @@ -527,7 +547,7 @@ func TestOnSignal(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD)}) if err != nil { t.Error(err) } @@ -574,12 +594,14 @@ func TestGetSnapshotAtTime(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } cp := currency.NewPair(currency.XRP, currency.DOGE) - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: cp}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: cp}) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } tt := time.Now() - s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][cp] + s, ok := p.exchangeAssetPairSettings[testExchange][asset.Spot][cp] if !ok { t.Fatal("couldn't get settings") } @@ -587,20 +609,20 @@ func TestGetSnapshotAtTime(t *testing.T) { Orders: []compliance.SnapshotOrder{ { Order: &gctorder.Detail{ - Exchange: "exch", + Exchange: testExchange, AssetType: asset.Spot, Pair: cp, Amount: 1337, }, }, }, - }, true) + }, false) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } e := &kline.Kline{ Base: event.Base{ - Exchange: "exch", + Exchange: testExchange, Time: tt, Interval: gctkline.OneDay, CurrencyPair: cp, @@ -628,11 +650,13 @@ func TestGetLatestSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } cp := currency.NewPair(currency.XRP, currency.DOGE) - err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: &ftx.FTX{}, Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: currency.NewPair(currency.XRP, currency.DOGE)}) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } - s, ok := p.exchangeAssetPairSettings["exch"][asset.Spot][cp] + s, ok := p.exchangeAssetPairSettings[testExchange][asset.Spot][cp] if !ok { t.Fatal("couldn't get settings") } @@ -640,14 +664,14 @@ func TestGetLatestSnapshot(t *testing.T) { Orders: []compliance.SnapshotOrder{ { Order: &gctorder.Detail{ - Exchange: "exch", + Exchange: testExchange, AssetType: asset.Spot, Pair: cp, Amount: 1337, }, }, }, - }, true) + }, false) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -660,14 +684,14 @@ func TestGetLatestSnapshot(t *testing.T) { Orders: []compliance.SnapshotOrder{ { Order: &gctorder.Detail{ - Exchange: "exch", + Exchange: testExchange, AssetType: asset.Spot, Pair: cp, Amount: 1337, }, }, }, - }, true) + }, false) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -679,8 +703,8 @@ func TestGetLatestSnapshot(t *testing.T) { if len(ss) != 1 { t.Fatal("expected 1") } - if len(ss[0].Orders) != 2 { - t.Error("expected 2") + if len(ss[0].Orders) != 1 { + t.Errorf("expected 1, received %v", len(ss[0].Orders)) } } @@ -693,7 +717,7 @@ func TestCalculatePNL(t *testing.T) { } exch := &ftx.FTX{} - exch.SetDefaults() + exch.Name = testExchange a := asset.Futures pair, _ := currency.NewPairFromStrings("BTC", "1231") err = p.SetupCurrencySettingsMap(&exchange.Settings{ diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 07ebf848368..a6b5c6d5d3b 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -48,23 +48,16 @@ type Portfolio struct { type Handler interface { OnSignal(signal.Event, *exchange.Settings, funding.IFundReserver) (*order.Order, error) OnFill(fill.Event, funding.IFundReleaser) (*fill.Fill, error) - GetLatestOrderSnapshotForEvent(common.EventHandler) (compliance.Snapshot, error) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) - ViewHoldingAtTimePeriod(common.EventHandler) (*holdings.Holding, error) setHoldingsForOffset(*holdings.Holding, bool) error UpdateHoldings(common.DataEventHandler, funding.IFundReleaser) error - GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) - - SetFee(string, asset.Item, currency.Pair, decimal.Decimal) GetFee(string, asset.Item, currency.Pair) decimal.Decimal - CalculatePNL(common.DataEventHandler) error GetLatestPNLForEvent(handler common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary - Reset() } diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go index cb030db1d49..aabfd63d547 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go @@ -14,7 +14,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" gctcommon "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) const ( @@ -31,6 +30,8 @@ const ( description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` ) +var errFuturesOnly = errors.New("can only work with futures") + // Strategy is an implementation of the Handler interface type Strategy struct { base.Strategy @@ -63,8 +64,8 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol return nil, common.ErrNilEvent } latest := d.Latest() - if latest.GetAssetType() != asset.Futures { - return nil, errors.New("can only work with futures") + if !latest.GetAssetType().IsFutures() { + return nil, errFuturesOnly } es, err := s.GetBaseData(d) diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go index aa3e64063e8..18244cba84e 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go @@ -83,7 +83,9 @@ func TestSetCustomSettings(t *testing.T) { func TestOnSignal(t *testing.T) { t.Parallel() - s := Strategy{} + s := Strategy{ + rsiPeriod: decimal.NewFromInt(14), + } _, err := s.OnSignal(nil, nil, nil) if !errors.Is(err, common.ErrNilEvent) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) @@ -91,8 +93,8 @@ func TestOnSignal(t *testing.T) { dStart := time.Date(2020, 1, 0, 0, 0, 0, 0, time.UTC) dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) dEnd := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) - exch := "binance" - a := asset.Spot + exch := "ftx" + a := asset.Futures p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ @@ -119,10 +121,9 @@ func TestOnSignal(t *testing.T) { } var resp signal.Event _, err = s.OnSignal(da, nil, nil) - if !errors.Is(err, base.ErrTooMuchBadData) { - t.Fatalf("expected: %v, received %v", base.ErrTooMuchBadData, err) + if !errors.Is(err, nil) { + t.Fatalf("expected: %v, received %v", nil, err) } - s.rsiPeriod = decimal.NewFromInt(1) _, err = s.OnSignal(da, nil, nil) if err != nil { @@ -158,7 +159,7 @@ func TestOnSignal(t *testing.T) { da.RangeHolder.SetHasDataFromCandles(da.Item.Candles) resp, err = s.OnSignal(da, nil, nil) if err != nil { - t.Error(err) + t.Fatal(err) } if resp.GetDirection() != common.DoNothing { t.Error("expected do nothing") @@ -173,7 +174,7 @@ func TestOnSignals(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - exch := "binance" + exch := "ftx" a := asset.Spot p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} @@ -198,9 +199,9 @@ func TestOnSignals(t *testing.T) { RangeHolder: &gctkline.IntervalRangeHolder{}, } _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) - if !strings.Contains(err.Error(), base.ErrTooMuchBadData.Error()) { + if !strings.Contains(err.Error(), base.ErrSimultaneousProcessingNotSupported.Error()) { // common.Errs type doesn't keep type - t.Errorf("received: %v, expected: %v", err, base.ErrTooMuchBadData) + t.Errorf("received: %v, expected: %v", err, base.ErrSimultaneousProcessingNotSupported) } } diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 4fc04be067a..26b95b6902e 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -12,6 +12,7 @@ import ( var ( // ErrNotCollateral is returned when a user requests collateral from a non-collateral pair ErrNotCollateral = errors.New("not a collateral pair") + ErrNilPair = errors.New("nil pair") ) func (c *Collateral) CanPlaceOrder(_ order.Side) bool { diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 7714d47659e..e02b183cbbf 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -34,7 +34,7 @@ func (i *Item) Release(amount, diff decimal.Decimal) error { if amount.LessThanOrEqual(decimal.Zero) { return errZeroAmountReceived } - if diff.IsNegative() && i.asset == asset.Spot { + if diff.IsNegative() && !i.asset.IsFutures() { return fmt.Errorf("%w diff", errNegativeAmountReceived) } if amount.GreaterThan(i.reserved) { diff --git a/backtester/funding/pair.go b/backtester/funding/pair.go index caa55e0c02f..0e5ad1ff397 100644 --- a/backtester/funding/pair.go +++ b/backtester/funding/pair.go @@ -119,6 +119,9 @@ func (p *Pair) FundReserver() IFundReserver { // GetPairReleaser func (p *Pair) GetPairReleaser() (IPairReleaser, error) { + if p == nil { + return nil, ErrNilPair + } return p, nil } From b730229c83e43aa1cefe727949a25352b6337ed4 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 19 Jan 2022 10:53:11 +1100 Subject: [PATCH 060/171] Splits the backtester setup and running into different files --- backtester/backtest/backtest.go | 842 ------------------------------- backtester/backtest/setup.go | 856 ++++++++++++++++++++++++++++++++ exchanges/exchange.go | 6 +- 3 files changed, 858 insertions(+), 846 deletions(-) create mode 100644 backtester/backtest/setup.go diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index cf1372f4f3a..4c40a813ad2 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -4,44 +4,23 @@ import ( "context" "errors" "fmt" - "path/filepath" - "runtime" - "strings" - "sync" "time" - "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/config" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" - "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/api" - "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/csv" - "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/database" "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/live" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange/slippage" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/risk" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/size" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" - "github.com/thrasher-corp/gocryptotrader/backtester/funding/trackingcurrencies" - "github.com/thrasher-corp/gocryptotrader/backtester/report" - gctcommon "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/common/convert" - gctconfig "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" - gctdatabase "github.com/thrasher-corp/gocryptotrader/database" - "github.com/thrasher-corp/gocryptotrader/engine" gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" @@ -73,827 +52,6 @@ func (bt *BackTest) Reset() { bt.databaseManager = nil } -// NewFromConfig takes a strategy config and configures a backtester variable to run -func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, error) { - log.Infoln(log.BackTester, "loading config...") - if cfg == nil { - return nil, errNilConfig - } - var err error - bt := New() - bt.exchangeManager = engine.SetupExchangeManager() - bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false) - if err != nil { - return nil, err - } - err = bt.orderManager.Start() - if err != nil { - return nil, err - } - if cfg.DataSettings.DatabaseData != nil { - bt.databaseManager, err = engine.SetupDatabaseConnectionManager(&cfg.DataSettings.DatabaseData.Config) - if err != nil { - return nil, err - } - } - - reports := &report.Data{ - Config: cfg, - TemplatePath: templatePath, - OutputPath: output, - } - bt.Reports = reports - - buyRule := exchange.MinMax{ - MinimumSize: cfg.PortfolioSettings.BuySide.MinimumSize, - MaximumSize: cfg.PortfolioSettings.BuySide.MaximumSize, - MaximumTotal: cfg.PortfolioSettings.BuySide.MaximumTotal, - } - sellRule := exchange.MinMax{ - MinimumSize: cfg.PortfolioSettings.SellSide.MinimumSize, - MaximumSize: cfg.PortfolioSettings.SellSide.MaximumSize, - MaximumTotal: cfg.PortfolioSettings.SellSide.MaximumTotal, - } - sizeManager := &size.Size{ - BuySide: buyRule, - SellSide: sellRule, - } - - funds := funding.SetupFundingManager( - cfg.StrategySettings.UseExchangeLevelFunding, - cfg.StrategySettings.DisableUSDTracking, - ) - if cfg.StrategySettings.UseExchangeLevelFunding { - for i := range cfg.StrategySettings.ExchangeLevelFunding { - var a asset.Item - a, err = asset.New(cfg.StrategySettings.ExchangeLevelFunding[i].Asset) - if err != nil { - return nil, err - } - cq := currency.NewCode(cfg.StrategySettings.ExchangeLevelFunding[i].Currency) - var item *funding.Item - item, err = funding.CreateItem(cfg.StrategySettings.ExchangeLevelFunding[i].ExchangeName, - a, - cq, - cfg.StrategySettings.ExchangeLevelFunding[i].InitialFunds, - cfg.StrategySettings.ExchangeLevelFunding[i].TransferFee, - cfg.StrategySettings.ExchangeLevelFunding[i].Collateral) - if err != nil { - return nil, err - } - err = funds.AddItem(item) - if err != nil { - return nil, err - } - } - } - - var emm = make(map[string]gctexchange.IBotExchange) - for i := range cfg.CurrencySettings { - _, ok := emm[cfg.CurrencySettings[i].ExchangeName] - if ok { - continue - } - var exch gctexchange.IBotExchange - exch, err = bt.exchangeManager.NewExchangeByName(cfg.CurrencySettings[i].ExchangeName) - if err != nil { - return nil, err - } - var conf *gctconfig.Exchange - conf, err = exch.GetDefaultConfig() - if err != nil { - return nil, err - } - conf.Enabled = true - // this is required because fuck you - conf.WebsocketTrafficTimeout = time.Second - conf.Websocket = convert.BoolPtr(false) - conf.WebsocketResponseCheckTimeout = time.Second - conf.WebsocketResponseMaxLimit = time.Second - err = exch.Setup(conf) - if err != nil { - return nil, err - } - - exchBase := exch.GetBase() - err = exch.UpdateTradablePairs(context.Background(), true) - if err != nil { - return nil, err - } - assets := exchBase.CurrencyPairs.GetAssetTypes(false) - for i := range assets { - if assets[i] != asset.Spot { - // only spot is supported at this time - continue - } - exchBase.CurrencyPairs.Pairs[assets[i]].AssetEnabled = convert.BoolPtr(true) - err = exch.SetPairs(exchBase.CurrencyPairs.Pairs[assets[i]].Available, assets[i], true) - if err != nil { - return nil, err - } - } - - bt.exchangeManager.Add(exch) - emm[cfg.CurrencySettings[i].ExchangeName] = exch - } - - portfolioRisk := &risk.Risk{ - CurrencySettings: make(map[string]map[asset.Item]map[currency.Pair]*risk.CurrencySettings), - } - - for i := range cfg.CurrencySettings { - if portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] == nil { - portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] = make(map[asset.Item]map[currency.Pair]*risk.CurrencySettings) - } - var a asset.Item - a, err = asset.New(cfg.CurrencySettings[i].Asset) - if err != nil { - return nil, fmt.Errorf( - "%w for %v %v %v. Err %v", - errInvalidConfigAsset, - cfg.CurrencySettings[i].ExchangeName, - cfg.CurrencySettings[i].Asset, - cfg.CurrencySettings[i].Base+cfg.CurrencySettings[i].Quote, - err) - } - if portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a] == nil { - portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a] = make(map[currency.Pair]*risk.CurrencySettings) - } - var curr currency.Pair - var b, q currency.Code - b = currency.NewCode(cfg.CurrencySettings[i].Base) - q = currency.NewCode(cfg.CurrencySettings[i].Quote) - curr = currency.NewPair(b, q) - var exch gctexchange.IBotExchange - exch, err = bt.exchangeManager.GetExchangeByName(cfg.CurrencySettings[i].ExchangeName) - if err != nil { - return nil, err - } - exchBase := exch.GetBase() - exchBase.Config.WebsocketTrafficTimeout = time.Second - exchBase.WebsocketResponseCheckTimeout = time.Second - - var requestFormat currency.PairFormat - requestFormat, err = exchBase.GetPairFormat(a, true) - if err != nil { - return nil, fmt.Errorf("could not format currency %v, %w", curr, err) - } - curr = curr.Format(requestFormat.Delimiter, requestFormat.Uppercase) - var avail, enabled currency.Pairs - avail, err = exch.GetAvailablePairs(a) - if err != nil { - return nil, fmt.Errorf("could not format currency %v, %w", curr, err) - } - enabled, err = exch.GetEnabledPairs(a) - if err != nil { - return nil, fmt.Errorf("could not format currency %v, %w", curr, err) - } - - avail = avail.Add(curr) - enabled = enabled.Add(curr) - err = exch.SetPairs(enabled, a, true) - if err != nil { - return nil, fmt.Errorf("could not format currency %v, %w", curr, err) - } - err = exch.SetPairs(avail, a, false) - if err != nil { - return nil, fmt.Errorf("could not format currency %v, %w", curr, err) - } - - portSet := &risk.CurrencySettings{ - MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, - } - if cfg.CurrencySettings[i].FuturesDetails != nil { - portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio - portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate - } - portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet - if cfg.CurrencySettings[i].MakerFee.GreaterThan(cfg.CurrencySettings[i].TakerFee) { - log.Warnf(log.BackTester, "maker fee '%v' should not exceed taker fee '%v'. Please review config", - cfg.CurrencySettings[i].MakerFee, - cfg.CurrencySettings[i].TakerFee) - } - - var baseItem, quoteItem, futureItem *funding.Item - if cfg.StrategySettings.UseExchangeLevelFunding { - switch { - case a == asset.Spot: - // add any remaining currency items that have no funding data in the strategy config - baseItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, - a, - b, - decimal.Zero, - decimal.Zero, - false) - if err != nil { - return nil, err - } - quoteItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, - a, - q, - decimal.Zero, - decimal.Zero, - false) - if err != nil { - return nil, err - } - err = funds.AddItem(baseItem) - if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { - return nil, err - } - err = funds.AddItem(quoteItem) - if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { - return nil, err - } - case a.IsFutures(): - if !futuresEnabled { - return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) - } - // setup contract items - c := funding.CreateFuturesCurrencyCode(b, q) - futureItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, - a, - c, - decimal.Zero, - decimal.Zero, - false) - if err != nil { - return nil, err - } - if cfg.CurrencySettings[i].FuturesDetails == nil { - return nil, errors.New("what the fuck") - } - err = funds.LinkCollateralCurrency(futureItem, currency.NewCode(cfg.CurrencySettings[i].FuturesDetails.CollateralCurrency)) - if err != nil { - return nil, err - } - err = funds.AddItem(futureItem) - if err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) - } - } else { - var bFunds, qFunds decimal.Decimal - if cfg.CurrencySettings[i].SpotDetails != nil { - if cfg.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { - bFunds = *cfg.CurrencySettings[i].SpotDetails.InitialBaseFunds - } - if cfg.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil { - qFunds = *cfg.CurrencySettings[i].SpotDetails.InitialQuoteFunds - } - } - baseItem, err = funding.CreateItem( - cfg.CurrencySettings[i].ExchangeName, - a, - curr.Base, - bFunds, - decimal.Zero, - false) - if err != nil { - return nil, err - } - quoteItem, err = funding.CreateItem( - cfg.CurrencySettings[i].ExchangeName, - a, - curr.Quote, - qFunds, - decimal.Zero, - false) - if err != nil { - return nil, err - } - var pair *funding.Pair - pair, err = funding.CreatePair(baseItem, quoteItem) - if err != nil { - return nil, err - } - err = funds.AddPair(pair) - if err != nil { - return nil, err - } - } - } - - bt.Funding = funds - var p *portfolio.Portfolio - p, err = portfolio.Setup(sizeManager, portfolioRisk, cfg.StatisticSettings.RiskFreeRate) - if err != nil { - return nil, err - } - - bt.Strategy, err = strategies.LoadStrategyByName(cfg.StrategySettings.Name, cfg.StrategySettings.SimultaneousSignalProcessing) - if err != nil { - return nil, err - } - bt.Strategy.SetDefaults() - if cfg.StrategySettings.CustomSettings != nil { - err = bt.Strategy.SetCustomSettings(cfg.StrategySettings.CustomSettings) - if err != nil && !errors.Is(err, base.ErrCustomSettingsUnsupported) { - return nil, err - } - } - stats := &statistics.Statistic{ - StrategyName: bt.Strategy.Name(), - StrategyNickname: cfg.Nickname, - StrategyDescription: bt.Strategy.Description(), - StrategyGoal: cfg.Goal, - ExchangeAssetPairStatistics: make(map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic), - RiskFreeRate: cfg.StatisticSettings.RiskFreeRate, - CandleInterval: gctkline.Interval(cfg.DataSettings.Interval), - FundManager: bt.Funding, - } - bt.Statistic = stats - reports.Statistics = stats - - if !cfg.StrategySettings.DisableUSDTracking { - var trackingPairs []trackingcurrencies.TrackingPair - for i := range cfg.CurrencySettings { - trackingPairs = append(trackingPairs, trackingcurrencies.TrackingPair{ - Exchange: cfg.CurrencySettings[i].ExchangeName, - Asset: cfg.CurrencySettings[i].Asset, - Base: cfg.CurrencySettings[i].Base, - Quote: cfg.CurrencySettings[i].Quote, - }) - } - trackingPairs, err = trackingcurrencies.CreateUSDTrackingPairs(trackingPairs, bt.exchangeManager) - if err != nil { - return nil, err - } - trackingPairCheck: - for i := range trackingPairs { - for j := range cfg.CurrencySettings { - if cfg.CurrencySettings[j].ExchangeName == trackingPairs[i].Exchange && - cfg.CurrencySettings[j].Asset == trackingPairs[i].Asset && - cfg.CurrencySettings[j].Base == trackingPairs[i].Base && - cfg.CurrencySettings[j].Quote == trackingPairs[i].Quote { - continue trackingPairCheck - } - } - cfg.CurrencySettings = append(cfg.CurrencySettings, config.CurrencySettings{ - ExchangeName: trackingPairs[i].Exchange, - Asset: trackingPairs[i].Asset, - Base: trackingPairs[i].Base, - Quote: trackingPairs[i].Quote, - USDTrackingPair: true, - }) - } - } - - e, err := bt.setupExchangeSettings(cfg) - if err != nil { - return nil, err - } - - bt.Exchange = &e - for i := range e.CurrencySettings { - err = p.SetupCurrencySettingsMap(&e.CurrencySettings[i]) - if err != nil { - return nil, err - } - } - bt.Portfolio = p - - cfg.PrintSetting() - - return bt, nil -} - -func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange, error) { - log.Infoln(log.BackTester, "setting exchange settings...") - resp := exchange.Exchange{} - - for i := range cfg.CurrencySettings { - exch, pair, a, err := bt.loadExchangePairAssetBase( - cfg.CurrencySettings[i].ExchangeName, - cfg.CurrencySettings[i].Base, - cfg.CurrencySettings[i].Quote, - cfg.CurrencySettings[i].Asset) - if err != nil { - return resp, err - } - - exchangeName := strings.ToLower(exch.GetName()) - bt.Datas.Setup() - klineData, err := bt.loadData(cfg, exch, pair, a, cfg.CurrencySettings[i].USDTrackingPair) - if err != nil { - return resp, err - } - - err = bt.Funding.AddUSDTrackingData(klineData) - if err != nil && - !errors.Is(err, trackingcurrencies.ErrCurrencyDoesNotContainsUSD) && - !errors.Is(err, funding.ErrUSDTrackingDisabled) { - return resp, err - } - - if !cfg.CurrencySettings[i].USDTrackingPair { - bt.Datas.SetDataForCurrency(exchangeName, a, pair, klineData) - var makerFee, takerFee decimal.Decimal - if cfg.CurrencySettings[i].MakerFee.GreaterThan(decimal.Zero) { - makerFee = cfg.CurrencySettings[i].MakerFee - } - if cfg.CurrencySettings[i].TakerFee.GreaterThan(decimal.Zero) { - takerFee = cfg.CurrencySettings[i].TakerFee - } - if makerFee.IsZero() || takerFee.IsZero() { - var apiMakerFee, apiTakerFee decimal.Decimal - apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair) - if makerFee.IsZero() { - makerFee = apiMakerFee - } - if takerFee.IsZero() { - takerFee = apiTakerFee - } - } - - if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(log.BackTester, "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", - cfg.CurrencySettings[i].MaximumSlippagePercent, - slippage.DefaultMaximumSlippagePercent) - cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent - } - if cfg.CurrencySettings[i].MaximumSlippagePercent.IsZero() { - cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent - } - if cfg.CurrencySettings[i].MinimumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(log.BackTester, "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", - cfg.CurrencySettings[i].MinimumSlippagePercent, - slippage.DefaultMinimumSlippagePercent) - cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent - } - if cfg.CurrencySettings[i].MinimumSlippagePercent.IsZero() { - cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent - } - if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(cfg.CurrencySettings[i].MinimumSlippagePercent) { - cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent - } - - realOrders := false - if cfg.DataSettings.LiveData != nil { - realOrders = cfg.DataSettings.LiveData.RealOrders - } - - buyRule := exchange.MinMax{ - MinimumSize: cfg.CurrencySettings[i].BuySide.MinimumSize, - MaximumSize: cfg.CurrencySettings[i].BuySide.MaximumSize, - MaximumTotal: cfg.CurrencySettings[i].BuySide.MaximumTotal, - } - sellRule := exchange.MinMax{ - MinimumSize: cfg.CurrencySettings[i].SellSide.MinimumSize, - MaximumSize: cfg.CurrencySettings[i].SellSide.MaximumSize, - MaximumTotal: cfg.CurrencySettings[i].SellSide.MaximumTotal, - } - - limits, err := exch.GetOrderExecutionLimits(a, pair) - if err != nil && !errors.Is(err, gctorder.ErrExchangeLimitNotLoaded) { - return resp, err - } - - if limits != nil { - if !cfg.CurrencySettings[i].CanUseExchangeLimits { - log.Warnf(log.BackTester, "exchange %s order execution limits supported but disabled for %s %s, live results may differ", - cfg.CurrencySettings[i].ExchangeName, - pair, - a) - cfg.CurrencySettings[i].ShowExchangeOrderLimitWarning = true - } - } - var lev exchange.Leverage - if cfg.CurrencySettings[i].FuturesDetails != nil { - lev = exchange.Leverage{ - CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, - MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, - MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, - } - } - resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ - Exchange: exch, - MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, - MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, - Pair: pair, - Asset: a, - ExchangeFee: takerFee, - MakerFee: takerFee, - TakerFee: makerFee, - UseRealOrders: realOrders, - BuySide: buyRule, - SellSide: sellRule, - Leverage: lev, - Limits: limits, - SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, - CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, - UseExchangePNLCalculation: cfg.CurrencySettings[i].UseExchangePNLCalculation, - }) - } - } - - return resp, nil -} - -func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { - e, err := bt.exchangeManager.GetExchangeByName(exch) - if err != nil { - return nil, currency.Pair{}, "", err - } - - var cp, fPair currency.Pair - cp, err = currency.NewPairFromStrings(base, quote) - if err != nil { - return nil, currency.Pair{}, "", err - } - - var a asset.Item - a, err = asset.New(ass) - if err != nil { - return nil, currency.Pair{}, "", err - } - - exchangeBase := e.GetBase() - if !exchangeBase.ValidateAPICredentials() { - log.Warnf(log.BackTester, "no credentials set for %v, this is theoretical only", exchangeBase.Name) - } - - fPair, err = exchangeBase.FormatExchangeCurrency(cp, a) - if err != nil { - return nil, currency.Pair{}, "", err - } - return e, fPair, a, nil -} - -// getFees will return an exchange's fee rate from GCT's wrapper function -func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency.Pair) (makerFee, takerFee decimal.Decimal) { - fTakerFee, err := exch.GetFeeByType(ctx, - &gctexchange.FeeBuilder{FeeType: gctexchange.OfflineTradeFee, - Pair: fPair, - IsMaker: false, - PurchasePrice: 1, - Amount: 1, - }) - if err != nil { - log.Errorf(log.BackTester, "Could not retrieve taker fee for %v. %v", exch.GetName(), err) - } - - fMakerFee, err := exch.GetFeeByType(ctx, - &gctexchange.FeeBuilder{ - FeeType: gctexchange.OfflineTradeFee, - Pair: fPair, - IsMaker: true, - PurchasePrice: 1, - Amount: 1, - }) - if err != nil { - log.Errorf(log.BackTester, "Could not retrieve maker fee for %v. %v", exch.GetName(), err) - } - - return decimal.NewFromFloat(fMakerFee), decimal.NewFromFloat(fTakerFee) -} - -// loadData will create kline data from the sources defined in start config files. It can exist from databases, csv or API endpoints -// it can also be generated from trade data which will be converted into kline data -func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, isUSDTrackingPair bool) (*kline.DataFromKline, error) { - if exch == nil { - return nil, engine.ErrExchangeNotFound - } - b := exch.GetBase() - if cfg.DataSettings.DatabaseData == nil && - cfg.DataSettings.LiveData == nil && - cfg.DataSettings.APIData == nil && - cfg.DataSettings.CSVData == nil { - return nil, errNoDataSource - } - if (cfg.DataSettings.APIData != nil && cfg.DataSettings.DatabaseData != nil) || - (cfg.DataSettings.APIData != nil && cfg.DataSettings.LiveData != nil) || - (cfg.DataSettings.APIData != nil && cfg.DataSettings.CSVData != nil) || - (cfg.DataSettings.DatabaseData != nil && cfg.DataSettings.LiveData != nil) || - (cfg.DataSettings.CSVData != nil && cfg.DataSettings.LiveData != nil) || - (cfg.DataSettings.CSVData != nil && cfg.DataSettings.DatabaseData != nil) { - return nil, errAmbiguousDataSource - } - - dataType, err := common.DataTypeToInt(cfg.DataSettings.DataType) - if err != nil { - return nil, err - } - - log.Infof(log.BackTester, "loading data for %v %v %v...\n", exch.GetName(), a, fPair) - resp := &kline.DataFromKline{} - switch { - case cfg.DataSettings.CSVData != nil: - if cfg.DataSettings.Interval <= 0 { - return nil, errIntervalUnset - } - resp, err = csv.LoadData( - dataType, - cfg.DataSettings.CSVData.FullPath, - strings.ToLower(exch.GetName()), - cfg.DataSettings.Interval, - fPair, - a, - isUSDTrackingPair) - if err != nil { - return nil, fmt.Errorf("%v. Please check your GoCryptoTrader configuration", err) - } - resp.Item.RemoveDuplicates() - resp.Item.SortCandlesByTimestamp(false) - resp.RangeHolder, err = gctkline.CalculateCandleDateRanges( - resp.Item.Candles[0].Time, - resp.Item.Candles[len(resp.Item.Candles)-1].Time.Add(cfg.DataSettings.Interval), - gctkline.Interval(cfg.DataSettings.Interval), - 0, - ) - if err != nil { - return nil, err - } - resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) - summary := resp.RangeHolder.DataSummary(false) - if len(summary) > 0 { - log.Warnf(log.BackTester, "%v", summary) - } - case cfg.DataSettings.DatabaseData != nil: - if cfg.DataSettings.DatabaseData.InclusiveEndDate { - cfg.DataSettings.DatabaseData.EndDate = cfg.DataSettings.DatabaseData.EndDate.Add(cfg.DataSettings.Interval) - } - if cfg.DataSettings.DatabaseData.Path == "" { - cfg.DataSettings.DatabaseData.Path = filepath.Join(gctcommon.GetDefaultDataDir(runtime.GOOS), "database") - } - gctdatabase.DB.DataPath = filepath.Join(cfg.DataSettings.DatabaseData.Path) - err = gctdatabase.DB.SetConfig(&cfg.DataSettings.DatabaseData.Config) - if err != nil { - return nil, err - } - err = bt.databaseManager.Start(&sync.WaitGroup{}) - if err != nil { - return nil, err - } - defer func() { - stopErr := bt.databaseManager.Stop() - if stopErr != nil { - log.Error(log.BackTester, stopErr) - } - }() - resp, err = loadDatabaseData(cfg, exch.GetName(), fPair, a, dataType, isUSDTrackingPair) - if err != nil { - return nil, fmt.Errorf("unable to retrieve data from GoCryptoTrader database. Error: %v. Please ensure the database is setup correctly and has data before use", err) - } - - resp.Item.RemoveDuplicates() - resp.Item.SortCandlesByTimestamp(false) - resp.RangeHolder, err = gctkline.CalculateCandleDateRanges( - cfg.DataSettings.DatabaseData.StartDate, - cfg.DataSettings.DatabaseData.EndDate, - gctkline.Interval(cfg.DataSettings.Interval), - 0, - ) - if err != nil { - return nil, err - } - resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) - summary := resp.RangeHolder.DataSummary(false) - if len(summary) > 0 { - log.Warnf(log.BackTester, "%v", summary) - } - case cfg.DataSettings.APIData != nil: - if cfg.DataSettings.APIData.InclusiveEndDate { - cfg.DataSettings.APIData.EndDate = cfg.DataSettings.APIData.EndDate.Add(cfg.DataSettings.Interval) - } - resp, err = loadAPIData( - cfg, - exch, - fPair, - a, - b.Features.Enabled.Kline.ResultLimit, - dataType) - if err != nil { - return resp, err - } - case cfg.DataSettings.LiveData != nil: - if isUSDTrackingPair { - return nil, errLiveUSDTrackingNotSupported - } - if len(cfg.CurrencySettings) > 1 { - return nil, errors.New("live data simulation only supports one currency") - } - err = loadLiveData(cfg, b) - if err != nil { - return nil, err - } - go bt.loadLiveDataLoop( - resp, - cfg, - exch, - fPair, - a, - dataType) - return resp, nil - } - if resp == nil { - return nil, fmt.Errorf("processing error, response returned nil") - } - - err = b.ValidateKline(fPair, a, resp.Item.Interval) - if err != nil { - if dataType != common.DataTrade || !strings.EqualFold(err.Error(), "interval not supported") { - return nil, err - } - } - - err = resp.Load() - if err != nil { - return nil, err - } - bt.Reports.AddKlineItem(&resp.Item) - return resp, nil -} - -func loadDatabaseData(cfg *config.Config, name string, fPair currency.Pair, a asset.Item, dataType int64, isUSDTrackingPair bool) (*kline.DataFromKline, error) { - if cfg == nil || cfg.DataSettings.DatabaseData == nil { - return nil, errors.New("nil config data received") - } - if cfg.DataSettings.Interval <= 0 { - return nil, errIntervalUnset - } - - return database.LoadData( - cfg.DataSettings.DatabaseData.StartDate, - cfg.DataSettings.DatabaseData.EndDate, - cfg.DataSettings.Interval, - strings.ToLower(name), - dataType, - fPair, - a, - isUSDTrackingPair) -} - -func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, resultLimit uint32, dataType int64) (*kline.DataFromKline, error) { - if cfg.DataSettings.Interval <= 0 { - return nil, errIntervalUnset - } - dates, err := gctkline.CalculateCandleDateRanges( - cfg.DataSettings.APIData.StartDate, - cfg.DataSettings.APIData.EndDate, - gctkline.Interval(cfg.DataSettings.Interval), - resultLimit) - if err != nil { - return nil, err - } - candles, err := api.LoadData(context.TODO(), - dataType, - cfg.DataSettings.APIData.StartDate, - cfg.DataSettings.APIData.EndDate, - cfg.DataSettings.Interval, - exch, - fPair, - a) - if err != nil { - return nil, fmt.Errorf("%v. Please check your GoCryptoTrader configuration", err) - } - dates.SetHasDataFromCandles(candles.Candles) - summary := dates.DataSummary(false) - if len(summary) > 0 { - log.Warnf(log.BackTester, "%v", summary) - } - candles.FillMissingDataWithEmptyEntries(dates) - candles.RemoveOutsideRange(cfg.DataSettings.APIData.StartDate, cfg.DataSettings.APIData.EndDate) - return &kline.DataFromKline{ - Item: *candles, - RangeHolder: dates, - }, nil -} - -func loadLiveData(cfg *config.Config, base *gctexchange.Base) error { - if cfg == nil || base == nil || cfg.DataSettings.LiveData == nil { - return common.ErrNilArguments - } - if cfg.DataSettings.Interval <= 0 { - return errIntervalUnset - } - - if cfg.DataSettings.LiveData.APIKeyOverride != "" { - base.API.Credentials.Key = cfg.DataSettings.LiveData.APIKeyOverride - } - if cfg.DataSettings.LiveData.APISecretOverride != "" { - base.API.Credentials.Secret = cfg.DataSettings.LiveData.APISecretOverride - } - if cfg.DataSettings.LiveData.APIClientIDOverride != "" { - base.API.Credentials.ClientID = cfg.DataSettings.LiveData.APIClientIDOverride - } - if cfg.DataSettings.LiveData.API2FAOverride != "" { - base.API.Credentials.PEMKey = cfg.DataSettings.LiveData.API2FAOverride - } - if cfg.DataSettings.LiveData.APISubAccountOverride != "" { - base.API.Credentials.Subaccount = cfg.DataSettings.LiveData.APISubAccountOverride - } - validated := base.ValidateAPICredentials() - base.API.AuthenticatedSupport = validated - if !validated && cfg.DataSettings.LiveData.RealOrders { - log.Warn(log.BackTester, "invalid API credentials set, real orders set to false") - cfg.DataSettings.LiveData.RealOrders = false - } - return nil -} - // Run will iterate over loaded data events // save them and then handle the event based on its type func (bt *BackTest) Run() error { diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go new file mode 100644 index 00000000000..5f8e3a54916 --- /dev/null +++ b/backtester/backtest/setup.go @@ -0,0 +1,856 @@ +package backtest + +import ( + "context" + "errors" + "fmt" + "path/filepath" + "runtime" + "strings" + "sync" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/config" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/api" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/csv" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/database" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange/slippage" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/risk" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/size" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" + "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/backtester/funding/trackingcurrencies" + "github.com/thrasher-corp/gocryptotrader/backtester/report" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" + gctconfig "github.com/thrasher-corp/gocryptotrader/config" + "github.com/thrasher-corp/gocryptotrader/currency" + gctdatabase "github.com/thrasher-corp/gocryptotrader/database" + "github.com/thrasher-corp/gocryptotrader/engine" + gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" +) + +// NewFromConfig takes a strategy config and configures a backtester variable to run +func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, error) { + log.Infoln(log.BackTester, "loading config...") + if cfg == nil { + return nil, errNilConfig + } + var err error + bt := New() + bt.exchangeManager = engine.SetupExchangeManager() + bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false) + if err != nil { + return nil, err + } + err = bt.orderManager.Start() + if err != nil { + return nil, err + } + if cfg.DataSettings.DatabaseData != nil { + bt.databaseManager, err = engine.SetupDatabaseConnectionManager(&cfg.DataSettings.DatabaseData.Config) + if err != nil { + return nil, err + } + } + + reports := &report.Data{ + Config: cfg, + TemplatePath: templatePath, + OutputPath: output, + } + bt.Reports = reports + + buyRule := exchange.MinMax{ + MinimumSize: cfg.PortfolioSettings.BuySide.MinimumSize, + MaximumSize: cfg.PortfolioSettings.BuySide.MaximumSize, + MaximumTotal: cfg.PortfolioSettings.BuySide.MaximumTotal, + } + sellRule := exchange.MinMax{ + MinimumSize: cfg.PortfolioSettings.SellSide.MinimumSize, + MaximumSize: cfg.PortfolioSettings.SellSide.MaximumSize, + MaximumTotal: cfg.PortfolioSettings.SellSide.MaximumTotal, + } + sizeManager := &size.Size{ + BuySide: buyRule, + SellSide: sellRule, + } + + funds := funding.SetupFundingManager( + cfg.StrategySettings.UseExchangeLevelFunding, + cfg.StrategySettings.DisableUSDTracking, + ) + if cfg.StrategySettings.UseExchangeLevelFunding { + for i := range cfg.StrategySettings.ExchangeLevelFunding { + var a asset.Item + a, err = asset.New(cfg.StrategySettings.ExchangeLevelFunding[i].Asset) + if err != nil { + return nil, err + } + cq := currency.NewCode(cfg.StrategySettings.ExchangeLevelFunding[i].Currency) + var item *funding.Item + item, err = funding.CreateItem(cfg.StrategySettings.ExchangeLevelFunding[i].ExchangeName, + a, + cq, + cfg.StrategySettings.ExchangeLevelFunding[i].InitialFunds, + cfg.StrategySettings.ExchangeLevelFunding[i].TransferFee, + cfg.StrategySettings.ExchangeLevelFunding[i].Collateral) + if err != nil { + return nil, err + } + err = funds.AddItem(item) + if err != nil { + return nil, err + } + } + } + + var emm = make(map[string]gctexchange.IBotExchange) + for i := range cfg.CurrencySettings { + _, ok := emm[cfg.CurrencySettings[i].ExchangeName] + if ok { + continue + } + var exch gctexchange.IBotExchange + exch, err = bt.exchangeManager.NewExchangeByName(cfg.CurrencySettings[i].ExchangeName) + if err != nil { + return nil, err + } + var conf *gctconfig.Exchange + conf, err = exch.GetDefaultConfig() + if err != nil { + return nil, err + } + conf.Enabled = true + // this is required because fuck you + conf.WebsocketTrafficTimeout = time.Second + conf.Websocket = convert.BoolPtr(false) + conf.WebsocketResponseCheckTimeout = time.Second + conf.WebsocketResponseMaxLimit = time.Second + err = exch.Setup(conf) + if err != nil { + return nil, err + } + + exchBase := exch.GetBase() + err = exch.UpdateTradablePairs(context.Background(), true) + if err != nil { + return nil, err + } + assets := exchBase.CurrencyPairs.GetAssetTypes(false) + for i := range assets { + exchBase.CurrencyPairs.Pairs[assets[i]].AssetEnabled = convert.BoolPtr(true) + err = exch.SetPairs(exchBase.CurrencyPairs.Pairs[assets[i]].Available, assets[i], true) + if err != nil { + return nil, err + } + } + + bt.exchangeManager.Add(exch) + emm[cfg.CurrencySettings[i].ExchangeName] = exch + } + + portfolioRisk := &risk.Risk{ + CurrencySettings: make(map[string]map[asset.Item]map[currency.Pair]*risk.CurrencySettings), + } + + for i := range cfg.CurrencySettings { + if portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] == nil { + portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] = make(map[asset.Item]map[currency.Pair]*risk.CurrencySettings) + } + var a asset.Item + a, err = asset.New(cfg.CurrencySettings[i].Asset) + if err != nil { + return nil, fmt.Errorf( + "%w for %v %v %v. Err %v", + errInvalidConfigAsset, + cfg.CurrencySettings[i].ExchangeName, + cfg.CurrencySettings[i].Asset, + cfg.CurrencySettings[i].Base+cfg.CurrencySettings[i].Quote, + err) + } + if portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a] == nil { + portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a] = make(map[currency.Pair]*risk.CurrencySettings) + } + var curr currency.Pair + var b, q currency.Code + b = currency.NewCode(cfg.CurrencySettings[i].Base) + q = currency.NewCode(cfg.CurrencySettings[i].Quote) + curr = currency.NewPair(b, q) + var exch gctexchange.IBotExchange + exch, err = bt.exchangeManager.GetExchangeByName(cfg.CurrencySettings[i].ExchangeName) + if err != nil { + return nil, err + } + exchBase := exch.GetBase() + var requestFormat currency.PairFormat + requestFormat, err = exchBase.GetPairFormat(a, true) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + curr = curr.Format(requestFormat.Delimiter, requestFormat.Uppercase) + var avail, enabled currency.Pairs + avail, err = exch.GetAvailablePairs(a) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + enabled, err = exch.GetEnabledPairs(a) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + + avail = avail.Add(curr) + enabled = enabled.Add(curr) + err = exch.SetPairs(enabled, a, true) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + err = exch.SetPairs(avail, a, false) + if err != nil { + return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + } + + portSet := &risk.CurrencySettings{ + MaximumHoldingRatio: cfg.CurrencySettings[i].MaximumHoldingsRatio, + } + if cfg.CurrencySettings[i].FuturesDetails != nil { + portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio + portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate + } + portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet + if cfg.CurrencySettings[i].MakerFee.GreaterThan(cfg.CurrencySettings[i].TakerFee) { + log.Warnf(log.BackTester, "maker fee '%v' should not exceed taker fee '%v'. Please review config", + cfg.CurrencySettings[i].MakerFee, + cfg.CurrencySettings[i].TakerFee) + } + + var baseItem, quoteItem, futureItem *funding.Item + if cfg.StrategySettings.UseExchangeLevelFunding { + switch { + case a == asset.Spot: + // add any remaining currency items that have no funding data in the strategy config + baseItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, + a, + b, + decimal.Zero, + decimal.Zero, + false) + if err != nil { + return nil, err + } + quoteItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, + a, + q, + decimal.Zero, + decimal.Zero, + false) + if err != nil { + return nil, err + } + err = funds.AddItem(baseItem) + if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { + return nil, err + } + err = funds.AddItem(quoteItem) + if err != nil && !errors.Is(err, funding.ErrAlreadyExists) { + return nil, err + } + case a.IsFutures(): + if !futuresEnabled { + return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) + } + // setup contract items + c := funding.CreateFuturesCurrencyCode(b, q) + futureItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, + a, + c, + decimal.Zero, + decimal.Zero, + false) + if err != nil { + return nil, err + } + if cfg.CurrencySettings[i].FuturesDetails == nil { + return nil, errors.New("what the fuck") + } + err = funds.LinkCollateralCurrency(futureItem, currency.NewCode(cfg.CurrencySettings[i].FuturesDetails.CollateralCurrency)) + if err != nil { + return nil, err + } + err = funds.AddItem(futureItem) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) + } + } else { + var bFunds, qFunds decimal.Decimal + if cfg.CurrencySettings[i].SpotDetails != nil { + if cfg.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { + bFunds = *cfg.CurrencySettings[i].SpotDetails.InitialBaseFunds + } + if cfg.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil { + qFunds = *cfg.CurrencySettings[i].SpotDetails.InitialQuoteFunds + } + } + baseItem, err = funding.CreateItem( + cfg.CurrencySettings[i].ExchangeName, + a, + curr.Base, + bFunds, + decimal.Zero, + false) + if err != nil { + return nil, err + } + quoteItem, err = funding.CreateItem( + cfg.CurrencySettings[i].ExchangeName, + a, + curr.Quote, + qFunds, + decimal.Zero, + false) + if err != nil { + return nil, err + } + var pair *funding.Pair + pair, err = funding.CreatePair(baseItem, quoteItem) + if err != nil { + return nil, err + } + err = funds.AddPair(pair) + if err != nil { + return nil, err + } + } + } + + bt.Funding = funds + var p *portfolio.Portfolio + p, err = portfolio.Setup(sizeManager, portfolioRisk, cfg.StatisticSettings.RiskFreeRate) + if err != nil { + return nil, err + } + + bt.Strategy, err = strategies.LoadStrategyByName(cfg.StrategySettings.Name, cfg.StrategySettings.SimultaneousSignalProcessing) + if err != nil { + return nil, err + } + bt.Strategy.SetDefaults() + if cfg.StrategySettings.CustomSettings != nil { + err = bt.Strategy.SetCustomSettings(cfg.StrategySettings.CustomSettings) + if err != nil && !errors.Is(err, base.ErrCustomSettingsUnsupported) { + return nil, err + } + } + stats := &statistics.Statistic{ + StrategyName: bt.Strategy.Name(), + StrategyNickname: cfg.Nickname, + StrategyDescription: bt.Strategy.Description(), + StrategyGoal: cfg.Goal, + ExchangeAssetPairStatistics: make(map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic), + RiskFreeRate: cfg.StatisticSettings.RiskFreeRate, + CandleInterval: gctkline.Interval(cfg.DataSettings.Interval), + FundManager: bt.Funding, + } + bt.Statistic = stats + reports.Statistics = stats + + if !cfg.StrategySettings.DisableUSDTracking { + var trackingPairs []trackingcurrencies.TrackingPair + for i := range cfg.CurrencySettings { + trackingPairs = append(trackingPairs, trackingcurrencies.TrackingPair{ + Exchange: cfg.CurrencySettings[i].ExchangeName, + Asset: cfg.CurrencySettings[i].Asset, + Base: cfg.CurrencySettings[i].Base, + Quote: cfg.CurrencySettings[i].Quote, + }) + } + trackingPairs, err = trackingcurrencies.CreateUSDTrackingPairs(trackingPairs, bt.exchangeManager) + if err != nil { + return nil, err + } + trackingPairCheck: + for i := range trackingPairs { + for j := range cfg.CurrencySettings { + if cfg.CurrencySettings[j].ExchangeName == trackingPairs[i].Exchange && + cfg.CurrencySettings[j].Asset == trackingPairs[i].Asset && + cfg.CurrencySettings[j].Base == trackingPairs[i].Base && + cfg.CurrencySettings[j].Quote == trackingPairs[i].Quote { + continue trackingPairCheck + } + } + cfg.CurrencySettings = append(cfg.CurrencySettings, config.CurrencySettings{ + ExchangeName: trackingPairs[i].Exchange, + Asset: trackingPairs[i].Asset, + Base: trackingPairs[i].Base, + Quote: trackingPairs[i].Quote, + USDTrackingPair: true, + }) + } + } + + e, err := bt.setupExchangeSettings(cfg) + if err != nil { + return nil, err + } + + bt.Exchange = &e + for i := range e.CurrencySettings { + err = p.SetupCurrencySettingsMap(&e.CurrencySettings[i]) + if err != nil { + return nil, err + } + } + bt.Portfolio = p + + cfg.PrintSetting() + + return bt, nil +} + +func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange, error) { + log.Infoln(log.BackTester, "setting exchange settings...") + resp := exchange.Exchange{} + + for i := range cfg.CurrencySettings { + exch, pair, a, err := bt.loadExchangePairAssetBase( + cfg.CurrencySettings[i].ExchangeName, + cfg.CurrencySettings[i].Base, + cfg.CurrencySettings[i].Quote, + cfg.CurrencySettings[i].Asset) + if err != nil { + return resp, err + } + + exchangeName := strings.ToLower(exch.GetName()) + bt.Datas.Setup() + klineData, err := bt.loadData(cfg, exch, pair, a, cfg.CurrencySettings[i].USDTrackingPair) + if err != nil { + return resp, err + } + + err = bt.Funding.AddUSDTrackingData(klineData) + if err != nil && + !errors.Is(err, trackingcurrencies.ErrCurrencyDoesNotContainsUSD) && + !errors.Is(err, funding.ErrUSDTrackingDisabled) { + return resp, err + } + + if !cfg.CurrencySettings[i].USDTrackingPair { + bt.Datas.SetDataForCurrency(exchangeName, a, pair, klineData) + var makerFee, takerFee decimal.Decimal + if cfg.CurrencySettings[i].MakerFee.GreaterThan(decimal.Zero) { + makerFee = cfg.CurrencySettings[i].MakerFee + } + if cfg.CurrencySettings[i].TakerFee.GreaterThan(decimal.Zero) { + takerFee = cfg.CurrencySettings[i].TakerFee + } + if makerFee.IsZero() || takerFee.IsZero() { + var apiMakerFee, apiTakerFee decimal.Decimal + apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair) + if makerFee.IsZero() { + makerFee = apiMakerFee + } + if takerFee.IsZero() { + takerFee = apiTakerFee + } + } + + if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(decimal.Zero) { + log.Warnf(log.BackTester, "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", + cfg.CurrencySettings[i].MaximumSlippagePercent, + slippage.DefaultMaximumSlippagePercent) + cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + } + if cfg.CurrencySettings[i].MaximumSlippagePercent.IsZero() { + cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + } + if cfg.CurrencySettings[i].MinimumSlippagePercent.LessThan(decimal.Zero) { + log.Warnf(log.BackTester, "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", + cfg.CurrencySettings[i].MinimumSlippagePercent, + slippage.DefaultMinimumSlippagePercent) + cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent + } + if cfg.CurrencySettings[i].MinimumSlippagePercent.IsZero() { + cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent + } + if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(cfg.CurrencySettings[i].MinimumSlippagePercent) { + cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + } + + realOrders := false + if cfg.DataSettings.LiveData != nil { + realOrders = cfg.DataSettings.LiveData.RealOrders + } + + buyRule := exchange.MinMax{ + MinimumSize: cfg.CurrencySettings[i].BuySide.MinimumSize, + MaximumSize: cfg.CurrencySettings[i].BuySide.MaximumSize, + MaximumTotal: cfg.CurrencySettings[i].BuySide.MaximumTotal, + } + sellRule := exchange.MinMax{ + MinimumSize: cfg.CurrencySettings[i].SellSide.MinimumSize, + MaximumSize: cfg.CurrencySettings[i].SellSide.MaximumSize, + MaximumTotal: cfg.CurrencySettings[i].SellSide.MaximumTotal, + } + + limits, err := exch.GetOrderExecutionLimits(a, pair) + if err != nil && !errors.Is(err, gctorder.ErrExchangeLimitNotLoaded) { + return resp, err + } + + if limits != nil { + if !cfg.CurrencySettings[i].CanUseExchangeLimits { + log.Warnf(log.BackTester, "exchange %s order execution limits supported but disabled for %s %s, live results may differ", + cfg.CurrencySettings[i].ExchangeName, + pair, + a) + cfg.CurrencySettings[i].ShowExchangeOrderLimitWarning = true + } + } + var lev exchange.Leverage + if cfg.CurrencySettings[i].FuturesDetails != nil { + lev = exchange.Leverage{ + CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, + MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, + MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, + } + } + resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ + Exchange: exch, + MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, + MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, + Pair: pair, + Asset: a, + ExchangeFee: takerFee, + MakerFee: takerFee, + TakerFee: makerFee, + UseRealOrders: realOrders, + BuySide: buyRule, + SellSide: sellRule, + Leverage: lev, + Limits: limits, + SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, + CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, + UseExchangePNLCalculation: cfg.CurrencySettings[i].UseExchangePNLCalculation, + }) + } + } + + return resp, nil +} + +func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { + e, err := bt.exchangeManager.GetExchangeByName(exch) + if err != nil { + return nil, currency.Pair{}, "", err + } + + var cp, fPair currency.Pair + cp, err = currency.NewPairFromStrings(base, quote) + if err != nil { + return nil, currency.Pair{}, "", err + } + + var a asset.Item + a, err = asset.New(ass) + if err != nil { + return nil, currency.Pair{}, "", err + } + + exchangeBase := e.GetBase() + if !exchangeBase.ValidateAPICredentials() { + log.Warnf(log.BackTester, "no credentials set for %v, this is theoretical only", exchangeBase.Name) + } + + fPair, err = exchangeBase.FormatExchangeCurrency(cp, a) + if err != nil { + return nil, currency.Pair{}, "", err + } + return e, fPair, a, nil +} + +// getFees will return an exchange's fee rate from GCT's wrapper function +func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency.Pair) (makerFee, takerFee decimal.Decimal) { + fTakerFee, err := exch.GetFeeByType(ctx, + &gctexchange.FeeBuilder{FeeType: gctexchange.OfflineTradeFee, + Pair: fPair, + IsMaker: false, + PurchasePrice: 1, + Amount: 1, + }) + if err != nil { + log.Errorf(log.BackTester, "Could not retrieve taker fee for %v. %v", exch.GetName(), err) + } + + fMakerFee, err := exch.GetFeeByType(ctx, + &gctexchange.FeeBuilder{ + FeeType: gctexchange.OfflineTradeFee, + Pair: fPair, + IsMaker: true, + PurchasePrice: 1, + Amount: 1, + }) + if err != nil { + log.Errorf(log.BackTester, "Could not retrieve maker fee for %v. %v", exch.GetName(), err) + } + + return decimal.NewFromFloat(fMakerFee), decimal.NewFromFloat(fTakerFee) +} + +// loadData will create kline data from the sources defined in start config files. It can exist from databases, csv or API endpoints +// it can also be generated from trade data which will be converted into kline data +func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, isUSDTrackingPair bool) (*kline.DataFromKline, error) { + if exch == nil { + return nil, engine.ErrExchangeNotFound + } + b := exch.GetBase() + if cfg.DataSettings.DatabaseData == nil && + cfg.DataSettings.LiveData == nil && + cfg.DataSettings.APIData == nil && + cfg.DataSettings.CSVData == nil { + return nil, errNoDataSource + } + if (cfg.DataSettings.APIData != nil && cfg.DataSettings.DatabaseData != nil) || + (cfg.DataSettings.APIData != nil && cfg.DataSettings.LiveData != nil) || + (cfg.DataSettings.APIData != nil && cfg.DataSettings.CSVData != nil) || + (cfg.DataSettings.DatabaseData != nil && cfg.DataSettings.LiveData != nil) || + (cfg.DataSettings.CSVData != nil && cfg.DataSettings.LiveData != nil) || + (cfg.DataSettings.CSVData != nil && cfg.DataSettings.DatabaseData != nil) { + return nil, errAmbiguousDataSource + } + + dataType, err := common.DataTypeToInt(cfg.DataSettings.DataType) + if err != nil { + return nil, err + } + + log.Infof(log.BackTester, "loading data for %v %v %v...\n", exch.GetName(), a, fPair) + resp := &kline.DataFromKline{} + switch { + case cfg.DataSettings.CSVData != nil: + if cfg.DataSettings.Interval <= 0 { + return nil, errIntervalUnset + } + resp, err = csv.LoadData( + dataType, + cfg.DataSettings.CSVData.FullPath, + strings.ToLower(exch.GetName()), + cfg.DataSettings.Interval, + fPair, + a, + isUSDTrackingPair) + if err != nil { + return nil, fmt.Errorf("%v. Please check your GoCryptoTrader configuration", err) + } + resp.Item.RemoveDuplicates() + resp.Item.SortCandlesByTimestamp(false) + resp.RangeHolder, err = gctkline.CalculateCandleDateRanges( + resp.Item.Candles[0].Time, + resp.Item.Candles[len(resp.Item.Candles)-1].Time.Add(cfg.DataSettings.Interval), + gctkline.Interval(cfg.DataSettings.Interval), + 0, + ) + if err != nil { + return nil, err + } + resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) + summary := resp.RangeHolder.DataSummary(false) + if len(summary) > 0 { + log.Warnf(log.BackTester, "%v", summary) + } + case cfg.DataSettings.DatabaseData != nil: + if cfg.DataSettings.DatabaseData.InclusiveEndDate { + cfg.DataSettings.DatabaseData.EndDate = cfg.DataSettings.DatabaseData.EndDate.Add(cfg.DataSettings.Interval) + } + if cfg.DataSettings.DatabaseData.Path == "" { + cfg.DataSettings.DatabaseData.Path = filepath.Join(gctcommon.GetDefaultDataDir(runtime.GOOS), "database") + } + gctdatabase.DB.DataPath = filepath.Join(cfg.DataSettings.DatabaseData.Path) + err = gctdatabase.DB.SetConfig(&cfg.DataSettings.DatabaseData.Config) + if err != nil { + return nil, err + } + err = bt.databaseManager.Start(&sync.WaitGroup{}) + if err != nil { + return nil, err + } + defer func() { + stopErr := bt.databaseManager.Stop() + if stopErr != nil { + log.Error(log.BackTester, stopErr) + } + }() + resp, err = loadDatabaseData(cfg, exch.GetName(), fPair, a, dataType, isUSDTrackingPair) + if err != nil { + return nil, fmt.Errorf("unable to retrieve data from GoCryptoTrader database. Error: %v. Please ensure the database is setup correctly and has data before use", err) + } + + resp.Item.RemoveDuplicates() + resp.Item.SortCandlesByTimestamp(false) + resp.RangeHolder, err = gctkline.CalculateCandleDateRanges( + cfg.DataSettings.DatabaseData.StartDate, + cfg.DataSettings.DatabaseData.EndDate, + gctkline.Interval(cfg.DataSettings.Interval), + 0, + ) + if err != nil { + return nil, err + } + resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) + summary := resp.RangeHolder.DataSummary(false) + if len(summary) > 0 { + log.Warnf(log.BackTester, "%v", summary) + } + case cfg.DataSettings.APIData != nil: + if cfg.DataSettings.APIData.InclusiveEndDate { + cfg.DataSettings.APIData.EndDate = cfg.DataSettings.APIData.EndDate.Add(cfg.DataSettings.Interval) + } + resp, err = loadAPIData( + cfg, + exch, + fPair, + a, + b.Features.Enabled.Kline.ResultLimit, + dataType) + if err != nil { + return resp, err + } + case cfg.DataSettings.LiveData != nil: + if isUSDTrackingPair { + return nil, errLiveUSDTrackingNotSupported + } + if len(cfg.CurrencySettings) > 1 { + return nil, errors.New("live data simulation only supports one currency") + } + err = loadLiveData(cfg, b) + if err != nil { + return nil, err + } + go bt.loadLiveDataLoop( + resp, + cfg, + exch, + fPair, + a, + dataType) + return resp, nil + } + if resp == nil { + return nil, fmt.Errorf("processing error, response returned nil") + } + + err = b.ValidateKline(fPair, a, resp.Item.Interval) + if err != nil { + if dataType != common.DataTrade || !strings.EqualFold(err.Error(), "interval not supported") { + return nil, err + } + } + + err = resp.Load() + if err != nil { + return nil, err + } + bt.Reports.AddKlineItem(&resp.Item) + return resp, nil +} + +func loadDatabaseData(cfg *config.Config, name string, fPair currency.Pair, a asset.Item, dataType int64, isUSDTrackingPair bool) (*kline.DataFromKline, error) { + if cfg == nil || cfg.DataSettings.DatabaseData == nil { + return nil, errors.New("nil config data received") + } + if cfg.DataSettings.Interval <= 0 { + return nil, errIntervalUnset + } + + return database.LoadData( + cfg.DataSettings.DatabaseData.StartDate, + cfg.DataSettings.DatabaseData.EndDate, + cfg.DataSettings.Interval, + strings.ToLower(name), + dataType, + fPair, + a, + isUSDTrackingPair) +} + +func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, resultLimit uint32, dataType int64) (*kline.DataFromKline, error) { + if cfg.DataSettings.Interval <= 0 { + return nil, errIntervalUnset + } + dates, err := gctkline.CalculateCandleDateRanges( + cfg.DataSettings.APIData.StartDate, + cfg.DataSettings.APIData.EndDate, + gctkline.Interval(cfg.DataSettings.Interval), + resultLimit) + if err != nil { + return nil, err + } + candles, err := api.LoadData(context.TODO(), + dataType, + cfg.DataSettings.APIData.StartDate, + cfg.DataSettings.APIData.EndDate, + cfg.DataSettings.Interval, + exch, + fPair, + a) + if err != nil { + return nil, fmt.Errorf("%v. Please check your GoCryptoTrader configuration", err) + } + dates.SetHasDataFromCandles(candles.Candles) + summary := dates.DataSummary(false) + if len(summary) > 0 { + log.Warnf(log.BackTester, "%v", summary) + } + candles.FillMissingDataWithEmptyEntries(dates) + candles.RemoveOutsideRange(cfg.DataSettings.APIData.StartDate, cfg.DataSettings.APIData.EndDate) + return &kline.DataFromKline{ + Item: *candles, + RangeHolder: dates, + }, nil +} + +func loadLiveData(cfg *config.Config, base *gctexchange.Base) error { + if cfg == nil || base == nil || cfg.DataSettings.LiveData == nil { + return common.ErrNilArguments + } + if cfg.DataSettings.Interval <= 0 { + return errIntervalUnset + } + + if cfg.DataSettings.LiveData.APIKeyOverride != "" { + base.API.Credentials.Key = cfg.DataSettings.LiveData.APIKeyOverride + } + if cfg.DataSettings.LiveData.APISecretOverride != "" { + base.API.Credentials.Secret = cfg.DataSettings.LiveData.APISecretOverride + } + if cfg.DataSettings.LiveData.APIClientIDOverride != "" { + base.API.Credentials.ClientID = cfg.DataSettings.LiveData.APIClientIDOverride + } + if cfg.DataSettings.LiveData.API2FAOverride != "" { + base.API.Credentials.PEMKey = cfg.DataSettings.LiveData.API2FAOverride + } + if cfg.DataSettings.LiveData.APISubAccountOverride != "" { + base.API.Credentials.Subaccount = cfg.DataSettings.LiveData.APISubAccountOverride + } + validated := base.ValidateAPICredentials() + base.API.AuthenticatedSupport = validated + if !validated && cfg.DataSettings.LiveData.RealOrders { + log.Warn(log.BackTester, "invalid API credentials set, real orders set to false") + cfg.DataSettings.LiveData.RealOrders = false + } + return nil +} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 559eafb0d7d..3242c7c39a6 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -55,10 +55,8 @@ var ( ) func (b *Base) checkAndInitRequester() { - if b.Requester == nil { - b.Requester = request.New(b.Name, - &http.Client{Transport: new(http.Transport)}) - } + b.Requester = request.New(b.Name, + &http.Client{Transport: new(http.Transport)}) } // SetHTTPClientTimeout sets the timeout value for the exchanges HTTP Client and From 558ab7021d95e7de6fbfaa103375ca33693fa6ab Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 19 Jan 2022 11:50:08 +1100 Subject: [PATCH 061/171] Merge, minor fixes --- backtester/backtest/backtest_test.go | 1 - backtester/config/config.go | 1 - backtester/config/configbuilder/main.go | 2 -- .../portfolio/compliance/compliance_test.go | 3 ++ .../eventhandlers/portfolio/portfolio.go | 35 +++++++++---------- .../eventhandlers/portfolio/portfolio_test.go | 13 ++++--- backtester/funding/funding_types.go | 1 - exchanges/exchange_test.go | 2 +- exchanges/order/futures_types.go | 2 ++ 9 files changed, 31 insertions(+), 29 deletions(-) diff --git a/backtester/backtest/backtest_test.go b/backtester/backtest/backtest_test.go index db675e90470..9cec6552a2f 100644 --- a/backtester/backtest/backtest_test.go +++ b/backtester/backtest/backtest_test.go @@ -76,7 +76,6 @@ func TestNewFromConfig(t *testing.T) { } cfg.CurrencySettings[0].Asset = asset.Spot.String() _, err = NewFromConfig(cfg, "", "") - _, err = NewFromConfig(cfg, "", "") if !errors.Is(err, base.ErrStrategyNotFound) { t.Errorf("received: %v, expected: %v", err, base.ErrStrategyNotFound) } diff --git a/backtester/config/config.go b/backtester/config/config.go index 8686f8d095c..2b5c40a50d9 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -96,7 +96,6 @@ func (c *Config) PrintSetting() { log.Infof(log.BackTester, "Sell rules: %+v", c.CurrencySettings[i].SellSide) if c.CurrencySettings[i].FuturesDetails != nil && c.CurrencySettings[i].Asset == asset.Futures.String() { log.Infof(log.BackTester, "Leverage rules: %+v", c.CurrencySettings[i].FuturesDetails.Leverage) - } log.Infof(log.BackTester, "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) } diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 3f0dde9c980..4a60c1e2681 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -600,7 +600,6 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* } } case asset.Futures.String(): - } fmt.Println("Enter the currency quote. eg USDT") @@ -621,7 +620,6 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* } } case asset.Futures.String(): - } fmt.Println("Enter the maker-fee. eg 0.001") diff --git a/backtester/eventhandlers/portfolio/compliance/compliance_test.go b/backtester/eventhandlers/portfolio/compliance/compliance_test.go index a65ffdee77a..8385642e5a8 100644 --- a/backtester/eventhandlers/portfolio/compliance/compliance_test.go +++ b/backtester/eventhandlers/portfolio/compliance/compliance_test.go @@ -102,6 +102,9 @@ func TestGetLatestSnapshot(t *testing.T) { Timestamp: tt.Add(time.Hour), Orders: nil, }, false) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } snappySnap = m.GetLatestSnapshot() if snappySnap.Timestamp.Equal(tt) { t.Errorf("expected %v", tt.Add(time.Hour)) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 5e6d51a54ec..5caf5f55543 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -196,18 +196,19 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return nil, err } sizingFunds = cReader.AvailableFunds() - sizingFunds, err = lookup.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ - CollateralCurrency: cReader.CollateralCurrency(), - Asset: ev.GetAssetType(), - Side: ev.GetDirection(), - CollateralAmount: sizingFunds, - USDPrice: ev.GetPrice(), - CalculateOffline: true, - }) + sizingFunds, err = lookup.Exchange.ScaleCollateral( + context.TODO(), "", + &gctorder.CollateralCalculator{ + CollateralCurrency: cReader.CollateralCurrency(), + Asset: ev.GetAssetType(), + Side: ev.GetDirection(), + CollateralAmount: sizingFunds, + USDPrice: ev.GetPrice(), + CalculateOffline: true, + }) if err != nil { return nil, err } - } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) @@ -599,12 +600,11 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er if len(positions) == 0 { return response, nil } - pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() - if err != nil { - return nil, err + pnlHistory := positions[len(positions)-1].PNLHistory + if len(pnlHistory) == 0 { + return response, nil } - - response.PNL = pnl + response.PNL = pnlHistory[len(pnlHistory)-1] return response, nil } @@ -630,11 +630,10 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { } positions := settings.FuturesTracker.GetPositions() if len(positions) > 0 { - pnl, err := positions[len(positions)-1].GetLatestPNLSnapshot() - if err != nil { - continue + pnlHistory := positions[len(positions)-1].PNLHistory + if len(pnlHistory) > 0 { + summary.PNL = pnlHistory[len(pnlHistory)-1] } - summary.PNL = pnl } result = append(result, summary) diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index d5566fe0df5..e043ea9b79d 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -675,7 +675,7 @@ func TestGetLatestSnapshot(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } - ss, err := p.GetLatestOrderSnapshots() + _, err = p.GetLatestOrderSnapshots() if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -696,7 +696,7 @@ func TestGetLatestSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, nil) } - ss, err = p.GetLatestOrderSnapshots() + ss, err := p.GetLatestOrderSnapshots() if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -766,6 +766,9 @@ func TestCalculatePNL(t *testing.T) { }, }, }, false) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } odCp := od.Copy() odCp.Price = od.Price - 1 odCp.Side = gctorder.Long @@ -790,11 +793,11 @@ func TestCalculatePNL(t *testing.T) { if len(pos) != 1 { t.Fatalf("expected one position, received '%v'", len(pos)) } - if len(pos[0].GetStats().PNLHistory) == 0 { + if len(pos[0].PNLHistory) == 0 { t.Fatal("expected a pnl entry ( ͡° ͜ʖ ͡°)") } - if !pos[0].GetStats().RealisedPNL.Equal(decimal.NewFromInt(20)) { + if !pos[0].RealisedPNL.Equal(decimal.NewFromInt(20)) { // 20 orders * $1 difference * 1x leverage - t.Errorf("expected 20, received '%v'", pos[0].GetStats().RealisedPNL) + t.Errorf("expected 20, received '%v'", pos[0].RealisedPNL) } } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 11b0b494d87..543f222fa8d 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -115,7 +115,6 @@ type Item struct { currency currency.Code initialFunds decimal.Decimal available decimal.Decimal - weightedAvailable decimal.Decimal reserved decimal.Decimal transferFee decimal.Decimal pairedWith *Item diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index feba2d86ba8..e0cfb779549 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -202,7 +202,7 @@ func TestHTTPClient(t *testing.T) { } if r.GetHTTPClient().Timeout != time.Second*5 { - t.Fatalf("TestHTTPClient unexpected value") + t.Fatalf("TestHTTPClient unexpected value, received %v", r.GetHTTPClient().Timeout) } r.Requester = nil diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 9c6897698b8..c409a3c7d3c 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -30,6 +30,8 @@ var ( ErrNotFuturesAsset = errors.New("asset type is not futures") // ErrUSDValueRequired returned when usd value unset ErrUSDValueRequired = errors.New("USD value required") + // ErrNotFutureAsset + ErrNotFutureAsset = errors.New("not a futures asset") errExchangeNameEmpty = errors.New("exchange name empty") errTimeUnset = errors.New("time unset") From ab1d85b98c8ceece12baf059dd5ba704be3b1f9a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 19 Jan 2022 17:11:25 +1100 Subject: [PATCH 062/171] Messing with some strategy updates --- backtester/backtest/backtest.go | 2 - backtester/backtest/setup.go | 2 + .../eventhandlers/portfolio/portfolio.go | 19 +++ .../portfolio/portfolio_types.go | 1 + .../ftxquarterlyfutures.go | 135 +++++++----------- engine/rpcserver.go | 4 +- exchanges/order/futures.go | 5 +- exchanges/order/futures_types.go | 25 ++-- 8 files changed, 89 insertions(+), 104 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 4c40a813ad2..e5897fa0fc6 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -28,8 +28,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) -var futuresEnabled = false - // New returns a new BackTest instance func New() *BackTest { return &BackTest{ diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go index 5f8e3a54916..0fa3a01c368 100644 --- a/backtester/backtest/setup.go +++ b/backtester/backtest/setup.go @@ -41,6 +41,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) +var futuresEnabled = true + // NewFromConfig takes a strategy config and configures a backtester variable to run func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, error) { log.Infoln(log.BackTester, "loading config...") diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 5caf5f55543..f98eef58f94 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -545,6 +545,25 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { return holdings.Holding{} } +// GetPositions returns all futures positions for an event's exchange, asset, pair +func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStats, error) { + if !e.GetAssetType().IsFutures() { + return nil, errors.New("not a future") + } + settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] + if !ok { + return nil, fmt.Errorf("%v %v %v %w", + e.GetExchange(), + e.GetAssetType(), + e.Pair(), + errNoPortfolioSettings) + } + if settings.FuturesTracker == nil { + return nil, errors.New("no futures tracker") + } + return settings.FuturesTracker.GetPositions(), nil +} + // CalculatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index a6b5c6d5d3b..538ab692885 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -55,6 +55,7 @@ type Handler interface { UpdateHoldings(common.DataEventHandler, funding.IFundReleaser) error GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) GetFee(string, asset.Item, currency.Pair) decimal.Decimal + GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) CalculatePNL(common.DataEventHandler) error GetLatestPNLForEvent(handler common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go index aabfd63d547..4409ff7e2d5 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go @@ -14,20 +14,16 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" gctcommon "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) const ( // Name is the strategy name - Name = "futures-rsi" - rsiPeriodKey = "rsi-period" - rsiLowKey = "rsi-low" - rsiHighKey = "rsi-high" - rsiStopLoss = "rsi-stop-loss" - rsiTakeProfit = "rsi-take-profit" - rsiTrailingStop = "rsi-trailing-stop" - rsiHighestUnrealised = "rsi-highest-unrealised" - rsiLowestUnrealised = "rsi-lowest-unrealised" - description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` + Name = "futures-rsi" + rsiPeriodKey = "rsi-period" + rsiLowKey = "rsi-low" + rsiHighKey = "rsi-high" + description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` ) var errFuturesOnly = errors.New("can only work with futures") @@ -35,14 +31,9 @@ var errFuturesOnly = errors.New("can only work with futures") // Strategy is an implementation of the Handler interface type Strategy struct { base.Strategy - rsiPeriod decimal.Decimal - rsiLow decimal.Decimal - rsiHigh decimal.Decimal - stopLoss decimal.Decimal - takeProfit decimal.Decimal - trailingStop decimal.Decimal - highestUnrealised decimal.Decimal - lowestUnrealised decimal.Decimal + rsiPeriod decimal.Decimal + rsiLow decimal.Decimal + rsiHigh decimal.Decimal } // Name returns the name of the strategy @@ -93,51 +84,51 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions. RSI %v", d.Latest().GetTime(), latestRSIValue)) return &es, nil } - /* + es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) - currentOrders, err := p.GetLatestOrderSnapshotForEvent(&es) - if err != nil { - return nil, err - } - var o *order.PositionTracker - for i := range currentOrders.Orders { - //if currentOrders.Orders[i].FuturesOrder != nil { - // if currentOrders.Orders[i].FuturesOrder.LongPositions == nil { - // if currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Short || currentOrders.Orders[i].FuturesOrder.CurrentDirection == order.Long { - // o = currentOrders.Orders[i].FuturesOrder - // } - // } - //} - } - if o.ShortPositions == nil { - switch { - case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): - es.SetDirection(order.Short) - case latestRSIValue.LessThanOrEqual(s.rsiLow): - es.SetDirection(order.Long) - default: - es.SetDirection(common.DoNothing) - } - es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) - } else { - price := decimal.NewFromFloat(o.ShortPositions.Price) - if latestRSIValue.LessThanOrEqual(s.rsiLow) || - latestRSIValue.GreaterThanOrEqual(s.rsiHigh) || - (!s.stopLoss.IsZero() && latest.GetClosePrice().LessThanOrEqual(s.stopLoss)) || - (!s.takeProfit.IsZero() && latest.GetClosePrice().GreaterThanOrEqual(s.takeProfit)) || - (!s.trailingStop.IsZero() && latest.GetClosePrice().Sub(price).Div(price).Mul(decimal.NewFromInt(100)).LessThanOrEqual(s.trailingStop)) || - o.ShortPositions.UnrealisedPNL.GreaterThanOrEqual(s.highestUnrealised) || - o.ShortPositions.UnrealisedPNL.LessThanOrEqual(s.lowestUnrealised) { - // set up the counter order to close the position - es.SetAmount(decimal.NewFromFloat(o.ShortPositions.Amount)) - if o.ShortPositions.Side == order.Short { + positions, err := p.GetPositions(&es) + if err != nil { + return nil, err + } + + if len(positions) > 0 { + latestPosition := positions[len(positions)-1] + if latestPosition.Status == order.Open && latestPosition.Exposure.GreaterThan(decimal.Zero) { + // close out the position + switch latestPosition.Direction { + case order.Short: + if latestRSIValue.LessThanOrEqual(s.rsiLow) { + // close out your positions! es.SetDirection(order.Long) - } else if o.ShortPositions.Side == order.Long { + es.SetAmount(latestPosition.Exposure) + } + if latestRSIValue.GreaterThanOrEqual(s.rsiHigh) { + // short your shorts? + } + case order.Long: + if latestRSIValue.LessThanOrEqual(s.rsiLow) { + // long your longs? + } + if latestRSIValue.GreaterThanOrEqual(s.rsiHigh) { + // close out your positions! es.SetDirection(order.Short) + es.SetAmount(latestPosition.Exposure) } + default: + es.SetDirection(common.DoNothing) } + return &es, nil } - */ + } + switch { + case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): + es.SetDirection(order.Short) + case latestRSIValue.LessThanOrEqual(s.rsiLow): + es.SetDirection(order.Long) + default: + es.SetDirection(common.DoNothing) + } + return &es, nil } @@ -176,36 +167,6 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) } s.rsiPeriod = decimal.NewFromFloat(rsiPeriod) - case rsiStopLoss: - sl, ok := v.(float64) - if !ok || sl <= 0 { - return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.stopLoss = decimal.NewFromFloat(sl) - case rsiTakeProfit: - tp, ok := v.(float64) - if !ok || tp <= 0 { - return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.takeProfit = decimal.NewFromFloat(tp) - case rsiTrailingStop: - ts, ok := v.(float64) - if !ok || ts <= 0 { - return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.trailingStop = decimal.NewFromFloat(ts) - case rsiHighestUnrealised: - ts, ok := v.(float64) - if !ok || ts <= 0 { - return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.highestUnrealised = decimal.NewFromFloat(ts) - case rsiLowestUnrealised: - ts, ok := v.(float64) - if !ok || ts <= 0 { - return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.lowestUnrealised = decimal.NewFromFloat(ts) default: return fmt.Errorf("%w unrecognised custom setting key %v with value %v. Cannot apply", base.ErrInvalidCustomSettings, k, v) } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 9a3ea056dbb..894041a1d9f 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4193,8 +4193,8 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if !pos[i].RealisedPNL.IsZero() { details.RealisedPNL = pos[i].RealisedPNL.String() } - if pos[i].LatestDirection != order.UnknownSide { - details.CurrentDirection = pos[i].LatestDirection.String() + if pos[i].Direction != order.UnknownSide { + details.CurrentDirection = pos[i].Direction.String() } if len(pos[i].PNLHistory) > 0 { details.OpeningDate = pos[i].PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 4936325a22d..146768468d2 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -290,10 +290,11 @@ func (p *PositionTracker) GetStats() PositionStats { Orders: append(p.longPositions, p.shortPositions...), RealisedPNL: p.realisedPNL, UnrealisedPNL: p.unrealisedPNL, - LatestDirection: p.currentDirection, + Direction: p.currentDirection, OpeningDirection: p.openingDirection, OpeningPrice: p.entryPrice, - LatestPrice: p.latestPrice, + Price: p.latestPrice, + Exposure: p.exposure, PNLHistory: p.pnlHistory, } } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index c409a3c7d3c..182fe48a9fa 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -215,17 +215,20 @@ type PNLResult struct { // PositionStats is a basic holder // for position information type PositionStats struct { - Exchange string - Asset asset.Item - Pair currency.Pair - Underlying currency.Code - Orders []Detail - RealisedPNL decimal.Decimal - UnrealisedPNL decimal.Decimal - LatestDirection Side - Status Status + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + Orders []Detail + RealisedPNL decimal.Decimal + UnrealisedPNL decimal.Decimal + Status Status + Direction Side + Price decimal.Decimal + Exposure decimal.Decimal + OpeningDirection Side OpeningPrice decimal.Decimal - LatestPrice decimal.Decimal - PNLHistory []PNLResult + + PNLHistory []PNLResult } From e437cfb89b4b33f2ab28c5a11437164ed48f7a55 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 20 Jan 2022 16:07:52 +1100 Subject: [PATCH 063/171] Failed understanding at collateral usage --- backtester/backtest/backtest.go | 2 +- backtester/backtest/setup.go | 27 +-- backtester/config/config_test.go | 2 - backtester/config/config_types.go | 7 - .../config/examples/futures-api-candles.strat | 10 +- backtester/eventhandlers/portfolio/helpers.go | 1 + .../eventhandlers/portfolio/portfolio.go | 194 +++++------------- backtester/eventhandlers/portfolio/setup.go | 97 +++++++++ .../eventhandlers/portfolio/size/size.go | 1 - .../ftxquarterlyfutures.go | 35 +--- .../ftxquarterlyfutures_types.go | 31 +++ backtester/funding/funding.go | 21 +- backtester/funding/funding_test.go | 64 +++--- exchanges/exchange.go | 5 + exchanges/ftx/ftx_wrapper.go | 8 + exchanges/order/futures_types.go | 1 + 16 files changed, 270 insertions(+), 236 deletions(-) create mode 100644 backtester/eventhandlers/portfolio/helpers.go create mode 100644 backtester/eventhandlers/portfolio/setup.go create mode 100644 backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index e5897fa0fc6..33a39a73a46 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -101,7 +101,6 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { if err != nil { return err } - switch eType := ev.(type) { case common.DataEventHandler: if bt.Strategy.UsingSimultaneousProcessing() { @@ -226,6 +225,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if err != nil { return err } + err = bt.Portfolio.CalculatePNL(ev) if err != nil { if errors.Is(err, gctorder.ErrPositionLiquidated) { diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go index 0fa3a01c368..e8445851018 100644 --- a/backtester/backtest/setup.go +++ b/backtester/backtest/setup.go @@ -106,8 +106,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, cq, cfg.StrategySettings.ExchangeLevelFunding[i].InitialFunds, - cfg.StrategySettings.ExchangeLevelFunding[i].TransferFee, - cfg.StrategySettings.ExchangeLevelFunding[i].Collateral) + cfg.StrategySettings.ExchangeLevelFunding[i].TransferFee) if err != nil { return nil, err } @@ -246,8 +245,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, b, decimal.Zero, - decimal.Zero, - false) + decimal.Zero) if err != nil { return nil, err } @@ -255,8 +253,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, q, decimal.Zero, - decimal.Zero, - false) + decimal.Zero) if err != nil { return nil, err } @@ -278,15 +275,21 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, c, decimal.Zero, - decimal.Zero, - false) + decimal.Zero) if err != nil { return nil, err } if cfg.CurrencySettings[i].FuturesDetails == nil { return nil, errors.New("what the fuck") } - err = funds.LinkCollateralCurrency(futureItem, currency.NewCode(cfg.CurrencySettings[i].FuturesDetails.CollateralCurrency)) + + var collateralCurrency currency.Code + collateralCurrency, err = exch.GetCollateralCurrencyForContract(a, currency.NewPair(b, q)) + if err != nil { + return nil, err + } + + err = funds.LinkCollateralCurrency(futureItem, collateralCurrency) if err != nil { return nil, err } @@ -312,8 +315,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, curr.Base, bFunds, - decimal.Zero, - false) + decimal.Zero) if err != nil { return nil, err } @@ -322,8 +324,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, a, curr.Quote, qFunds, - decimal.Zero, - false) + decimal.Zero) if err != nil { return nil, err } diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index fa9e37b7566..075fb4b78f1 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1214,7 +1214,6 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { Currency: "usdt", InitialFunds: *initialQuoteFunds2, TransferFee: decimal.Zero, - Collateral: true, }, }, }, @@ -1225,7 +1224,6 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { Base: "BTC", Quote: "0924", FuturesDetails: &FuturesDetails{ - CollateralCurrency: "USDT", Leverage: Leverage{ CanUseLeverage: true, MaximumOrderLeverageRate: makerFee, diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 4cd4367dc70..aaf71eefa3c 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -76,7 +76,6 @@ type ExchangeLevelFunding struct { Currency string `json:"currency"` InitialFunds decimal.Decimal `json:"initial-funds"` TransferFee decimal.Decimal `json:"transfer-fee"` - Collateral bool `json:"collateral"` } // StatisticSettings adjusts ratios where @@ -153,12 +152,6 @@ type SpotDetails struct { type FuturesDetails struct { Leverage Leverage `json:"leverage"` - // CollateralCurrency if the asset is futures, then this field - // allows a user to nominate another currency to be used as collateral - // while trading futures contracts - // This currency is a reference to a funding item setup in - // strategy - CollateralCurrency string `json:"collateral-currency"` } // APIData defines all fields to configure API based data diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/futures-api-candles.strat index 26099aee9c2..555d9d96d55 100644 --- a/backtester/config/examples/futures-api-candles.strat +++ b/backtester/config/examples/futures-api-candles.strat @@ -3,16 +3,15 @@ "goal": "To demonstrate DCA strategy using API candles", "strategy-settings": { "name": "dollarcostaverage", - "use-simultaneous-signal-processing": true, + "use-simultaneous-signal-processing": false, "use-exchange-level-funding": true, "exchange-level-funding": [ { "exchange-name": "ftx", "asset": "futures", - "currency": "usdt", + "currency": "usd", "initial-funds": "100000", - "transfer-fee": "0", - "collateral": true + "transfer-fee": "0" } ], "disable-usd-tracking": true @@ -29,8 +28,7 @@ "maximum-orders-with-leverage-ratio": "0", "maximum-leverage-rate": "0.001", "maximum-collateral-leverage-rate": "0" - }, - "collateral-currency": "USDT" + } }, "buy-side": { "minimum-size": "0.005", diff --git a/backtester/eventhandlers/portfolio/helpers.go b/backtester/eventhandlers/portfolio/helpers.go new file mode 100644 index 00000000000..68cd9cc499a --- /dev/null +++ b/backtester/eventhandlers/portfolio/helpers.go @@ -0,0 +1 @@ +package portfolio diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index f98eef58f94..b472d0be7f1 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "strings" "time" "github.com/shopspring/decimal" @@ -12,7 +11,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/risk" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" @@ -24,90 +22,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) -// Setup creates a portfolio manager instance and sets private fields -func Setup(sh SizeHandler, r risk.Handler, riskFreeRate decimal.Decimal) (*Portfolio, error) { - if sh == nil { - return nil, errSizeManagerUnset - } - if riskFreeRate.IsNegative() { - return nil, errNegativeRiskFreeRate - } - if r == nil { - return nil, errRiskManagerUnset - } - p := &Portfolio{} - p.sizeManager = sh - p.riskManager = r - p.riskFreeRate = riskFreeRate - - return p, nil -} - -// Reset returns the portfolio manager to its default state -func (p *Portfolio) Reset() { - p.exchangeAssetPairSettings = nil -} - -// SetupCurrencySettingsMap ensures a map is created and no panics happen -func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { - if setup == nil { - return errNoPortfolioSettings - } - if setup.Exchange == nil { - return errExchangeUnset - } - if setup.Asset == "" { - return errAssetUnset - } - if setup.Pair.IsEmpty() { - return errCurrencyPairUnset - } - if p.exchangeAssetPairSettings == nil { - p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) - } - name := strings.ToLower(setup.Exchange.GetName()) - if p.exchangeAssetPairSettings[name] == nil { - p.exchangeAssetPairSettings[name] = make(map[asset.Item]map[currency.Pair]*Settings) - } - if p.exchangeAssetPairSettings[name][setup.Asset] == nil { - p.exchangeAssetPairSettings[name][setup.Asset] = make(map[currency.Pair]*Settings) - } - if _, ok := p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair]; ok { - return nil - } - - settings := &Settings{ - Fee: setup.ExchangeFee, - BuySideSizing: setup.BuySide, - SellSideSizing: setup.SellSide, - Leverage: setup.Leverage, - ComplianceManager: compliance.Manager{ - Snapshots: []compliance.Snapshot{}, - }, - Exchange: setup.Exchange, - } - if setup.Asset.IsFutures() { - futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ - Exchange: name, - Asset: setup.Asset, - Pair: setup.Pair, - Underlying: setup.Pair.Base, - OfflineCalculation: true, - UseExchangePNLCalculation: setup.UseExchangePNLCalculation, - } - if setup.UseExchangePNLCalculation { - futureTrackerSetup.ExchangePNLCalculation = setup.Exchange - } - tracker, err := gctorder.SetupMultiPositionTracker(futureTrackerSetup) - if err != nil { - return err - } - settings.FuturesTracker = tracker - } - p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair] = settings - return nil -} - // OnSignal receives the event from the strategy on whether it has signalled to buy, do nothing or sell // on buy/sell, the portfolio manager will size the order and assess the risk of the order // if successful, it will pass on an order.Order to be used by the exchange event handler to place an order based on @@ -349,31 +263,6 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. return fe, nil } -// GetLatestOrderSnapshotForEvent gets orders related to the event -func (p *Portfolio) GetLatestOrderSnapshotForEvent(e common.EventHandler) (compliance.Snapshot, error) { - eapSettings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] - if !ok { - return compliance.Snapshot{}, fmt.Errorf("%w for %v %v %v", errNoPortfolioSettings, e.GetExchange(), e.GetAssetType(), e.Pair()) - } - return eapSettings.ComplianceManager.GetLatestSnapshot(), nil -} - -// GetLatestOrderSnapshots returns the latest snapshots from all stored pair data -func (p *Portfolio) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) { - var resp []compliance.Snapshot - for _, exchangeMap := range p.exchangeAssetPairSettings { - for _, assetMap := range exchangeMap { - for _, pairMap := range assetMap { - resp = append(resp, pairMap.ComplianceManager.GetLatestSnapshot()) - } - } - } - if len(resp) == 0 { - return nil, errNoPortfolioSettings - } - return resp, nil -} - // addComplianceSnapshot gets the previous snapshot of compliance events, updates with the latest fillevent // then saves the snapshot to the c func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { @@ -406,6 +295,60 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { return complianceManager.AddSnapshot(snap, false) } +func (p *Portfolio) setHoldingsForOffset(h *holdings.Holding, overwriteExisting bool) error { + if h.Timestamp.IsZero() { + return errHoldingsNoTimestamp + } + lookup, ok := p.exchangeAssetPairSettings[h.Exchange][h.Asset][h.Pair] + if !ok { + return fmt.Errorf("%w for %v %v %v", errNoPortfolioSettings, h.Exchange, h.Asset, h.Pair) + } + + if overwriteExisting && len(lookup.HoldingsSnapshots) == 0 { + return errNoHoldings + } + for i := len(lookup.HoldingsSnapshots) - 1; i >= 0; i-- { + if lookup.HoldingsSnapshots[i].Offset == h.Offset { + if overwriteExisting { + lookup.HoldingsSnapshots[i] = *h + return nil + } + return errHoldingsAlreadySet + } + } + if overwriteExisting { + return fmt.Errorf("%w at %v", errNoHoldings, h.Timestamp) + } + + lookup.HoldingsSnapshots = append(lookup.HoldingsSnapshots, *h) + return nil +} + +// GetLatestOrderSnapshotForEvent gets orders related to the event +func (p *Portfolio) GetLatestOrderSnapshotForEvent(e common.EventHandler) (compliance.Snapshot, error) { + eapSettings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] + if !ok { + return compliance.Snapshot{}, fmt.Errorf("%w for %v %v %v", errNoPortfolioSettings, e.GetExchange(), e.GetAssetType(), e.Pair()) + } + return eapSettings.ComplianceManager.GetLatestSnapshot(), nil +} + +// GetLatestOrderSnapshots returns the latest snapshots from all stored pair data +func (p *Portfolio) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) { + var resp []compliance.Snapshot + for _, exchangeMap := range p.exchangeAssetPairSettings { + for _, assetMap := range exchangeMap { + for _, pairMap := range assetMap { + resp = append(resp, pairMap.ComplianceManager.GetLatestSnapshot()) + } + } + } + if len(resp) == 0 { + return nil, errNoPortfolioSettings + } + return resp, nil +} + // GetComplianceManager returns the order snapshots for a given exchange, asset, pair func (p *Portfolio) GetComplianceManager(exchangeName string, a asset.Item, cp currency.Pair) (*compliance.Manager, error) { lookup := p.exchangeAssetPairSettings[exchangeName][a][cp] @@ -476,35 +419,6 @@ func (p *Portfolio) GetLatestHoldingsForAllCurrencies() []holdings.Holding { return resp } -func (p *Portfolio) setHoldingsForOffset(h *holdings.Holding, overwriteExisting bool) error { - if h.Timestamp.IsZero() { - return errHoldingsNoTimestamp - } - lookup, ok := p.exchangeAssetPairSettings[h.Exchange][h.Asset][h.Pair] - if !ok { - return fmt.Errorf("%w for %v %v %v", errNoPortfolioSettings, h.Exchange, h.Asset, h.Pair) - } - - if overwriteExisting && len(lookup.HoldingsSnapshots) == 0 { - return errNoHoldings - } - for i := len(lookup.HoldingsSnapshots) - 1; i >= 0; i-- { - if lookup.HoldingsSnapshots[i].Offset == h.Offset { - if overwriteExisting { - lookup.HoldingsSnapshots[i] = *h - return nil - } - return errHoldingsAlreadySet - } - } - if overwriteExisting { - return fmt.Errorf("%w at %v", errNoHoldings, h.Timestamp) - } - - lookup.HoldingsSnapshots = append(lookup.HoldingsSnapshots, *h) - return nil -} - // ViewHoldingAtTimePeriod retrieves a snapshot of holdings at a specific time period, // returning empty when not found func (p *Portfolio) ViewHoldingAtTimePeriod(ev common.EventHandler) (*holdings.Holding, error) { diff --git a/backtester/eventhandlers/portfolio/setup.go b/backtester/eventhandlers/portfolio/setup.go new file mode 100644 index 00000000000..3bd7a00043d --- /dev/null +++ b/backtester/eventhandlers/portfolio/setup.go @@ -0,0 +1,97 @@ +package portfolio + +import ( + "strings" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/risk" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +// Setup creates a portfolio manager instance and sets private fields +func Setup(sh SizeHandler, r risk.Handler, riskFreeRate decimal.Decimal) (*Portfolio, error) { + if sh == nil { + return nil, errSizeManagerUnset + } + if riskFreeRate.IsNegative() { + return nil, errNegativeRiskFreeRate + } + if r == nil { + return nil, errRiskManagerUnset + } + p := &Portfolio{} + p.sizeManager = sh + p.riskManager = r + p.riskFreeRate = riskFreeRate + + return p, nil +} + +// Reset returns the portfolio manager to its default state +func (p *Portfolio) Reset() { + p.exchangeAssetPairSettings = nil +} + +// SetupCurrencySettingsMap ensures a map is created and no panics happen +func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { + if setup == nil { + return errNoPortfolioSettings + } + if setup.Exchange == nil { + return errExchangeUnset + } + if setup.Asset == "" { + return errAssetUnset + } + if setup.Pair.IsEmpty() { + return errCurrencyPairUnset + } + if p.exchangeAssetPairSettings == nil { + p.exchangeAssetPairSettings = make(map[string]map[asset.Item]map[currency.Pair]*Settings) + } + name := strings.ToLower(setup.Exchange.GetName()) + if p.exchangeAssetPairSettings[name] == nil { + p.exchangeAssetPairSettings[name] = make(map[asset.Item]map[currency.Pair]*Settings) + } + if p.exchangeAssetPairSettings[name][setup.Asset] == nil { + p.exchangeAssetPairSettings[name][setup.Asset] = make(map[currency.Pair]*Settings) + } + if _, ok := p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair]; ok { + return nil + } + + settings := &Settings{ + Fee: setup.ExchangeFee, + BuySideSizing: setup.BuySide, + SellSideSizing: setup.SellSide, + Leverage: setup.Leverage, + ComplianceManager: compliance.Manager{ + Snapshots: []compliance.Snapshot{}, + }, + Exchange: setup.Exchange, + } + if setup.Asset.IsFutures() { + futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ + Exchange: name, + Asset: setup.Asset, + Pair: setup.Pair, + Underlying: setup.Pair.Base, + OfflineCalculation: true, + UseExchangePNLCalculation: setup.UseExchangePNLCalculation, + } + if setup.UseExchangePNLCalculation { + futureTrackerSetup.ExchangePNLCalculation = setup.Exchange + } + tracker, err := gctorder.SetupMultiPositionTracker(futureTrackerSetup) + if err != nil { + return err + } + settings.FuturesTracker = tracker + } + p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair] = settings + return nil +} diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 75f75a46559..d4176137ca3 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -41,7 +41,6 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if amount.GreaterThan(portfolioSize) { amount = portfolioSize } - case gctorder.Sell: // check size against currency specific settings amount, err = s.calculateSellSize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go index 4409ff7e2d5..cb3b649fcd9 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go @@ -1,8 +1,8 @@ package ftxquarterlyfutures import ( - "errors" "fmt" + "strings" "time" "github.com/shopspring/decimal" @@ -17,25 +17,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) -const ( - // Name is the strategy name - Name = "futures-rsi" - rsiPeriodKey = "rsi-period" - rsiLowKey = "rsi-low" - rsiHighKey = "rsi-high" - description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` -) - -var errFuturesOnly = errors.New("can only work with futures") - -// Strategy is an implementation of the Handler interface -type Strategy struct { - base.Strategy - rsiPeriod decimal.Decimal - rsiLow decimal.Decimal - rsiHigh decimal.Decimal -} - // Name returns the name of the strategy func (s *Strategy) Name() string { return Name @@ -58,6 +39,9 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol if !latest.GetAssetType().IsFutures() { return nil, errFuturesOnly } + if !strings.EqualFold(latest.GetExchange(), exchangeName) { + return nil, errOnlyFTXSupported + } es, err := s.GetBaseData(d) if err != nil { @@ -101,23 +85,16 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfol // close out your positions! es.SetDirection(order.Long) es.SetAmount(latestPosition.Exposure) - } - if latestRSIValue.GreaterThanOrEqual(s.rsiHigh) { - // short your shorts? + return &es, nil } case order.Long: - if latestRSIValue.LessThanOrEqual(s.rsiLow) { - // long your longs? - } if latestRSIValue.GreaterThanOrEqual(s.rsiHigh) { // close out your positions! es.SetDirection(order.Short) es.SetAmount(latestPosition.Exposure) + return &es, nil } - default: - es.SetDirection(common.DoNothing) } - return &es, nil } } switch { diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go new file mode 100644 index 00000000000..3413bb1674b --- /dev/null +++ b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go @@ -0,0 +1,31 @@ +package ftxquarterlyfutures + +import ( + "errors" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" +) + +const ( + // Name is the strategy name + Name = "futures-rsi" + rsiPeriodKey = "rsi-period" + rsiLowKey = "rsi-low" + rsiHighKey = "rsi-high" + description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` + exchangeName = "ftx" +) + +var ( + errFuturesOnly = errors.New("can only work with futures") + errOnlyFTXSupported = errors.New("only FTX supported for this strategy") +) + +// Strategy is an implementation of the Handler interface +type Strategy struct { + base.Strategy + rsiPeriod decimal.Decimal + rsiLow decimal.Decimal + rsiHigh decimal.Decimal +} diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 0a2d94121d2..c48525c5666 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -53,7 +53,7 @@ func CreateFuturesCurrencyCode(b, q currency.Code) currency.Code { } // CreateItem creates a new funding item -func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, transferFee decimal.Decimal, isCollateral bool) (*Item, error) { +func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, transferFee decimal.Decimal) (*Item, error) { if initialFunds.IsNegative() { return nil, fmt.Errorf("%v %v %v %w initial funds: %v", exch, a, ci, errNegativeAmountReceived, initialFunds) } @@ -69,7 +69,6 @@ func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, trans available: initialFunds, transferFee: transferFee, snapshot: make(map[time.Time]ItemSnapshot), - collateral: isCollateral, }, nil } @@ -85,7 +84,19 @@ func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) err return nil } } - return ErrFundsNotFound + collateral := &Item{ + exchange: item.exchange, + asset: item.asset, + currency: code, + pairedWith: item, + collateral: true, + } + err := f.AddItem(collateral) + if err != nil { + return err + } + item.pairedWith = collateral + return nil } // CreateSnapshot creates a Snapshot for an event's point in time @@ -164,7 +175,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { for j := range usdCandles.Candles { // usd stablecoins do not always match in value, // this is a simplified implementation that can allow - // USD tracking for many different currencies across many exchanges + // USD tracking for many currencies across many exchanges // without retrieving n candle history and exchange rates usdCandles.Candles[j].Open = 1 usdCandles.Candles[j].High = 1 @@ -365,7 +376,7 @@ func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pai var collat Collateral for i := range f.items { if a.IsFutures() { - if f.items[i].BasicEqual(exch, a, currency.NewCode(p.String()), currency.USDT) { + if f.items[i].MatchesCurrency(currency.NewCode(p.String())) { collat.Contract = f.items[i] collat.Collateral = f.items[i].pairedWith return &collat, nil diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 0b627aa5455..813076a2b8d 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -46,7 +46,7 @@ func TestSetupFundingManager(t *testing.T) { func TestReset(t *testing.T) { t.Parallel() f := SetupFundingManager(true, false) - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -136,7 +136,7 @@ func TestAddItem(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -158,7 +158,7 @@ func TestExists(t *testing.T) { if exists { t.Errorf("received '%v' expected '%v'", exists, false) } - conflictingSingleItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) + conflictingSingleItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -170,11 +170,11 @@ func TestExists(t *testing.T) { if !exists { t.Errorf("received '%v' expected '%v'", exists, true) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -243,11 +243,11 @@ func TestExists(t *testing.T) { func TestAddPair(t *testing.T) { t.Parallel() f := FundManager{} - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -297,11 +297,11 @@ func TestGetFundingForEvent(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -326,7 +326,7 @@ func TestGetFundingForEAC(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -352,11 +352,11 @@ func TestGetFundingForEAP(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -393,11 +393,11 @@ func TestGetFundingForEAP(t *testing.T) { func TestBaseInitialFunds(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -412,11 +412,11 @@ func TestBaseInitialFunds(t *testing.T) { func TestQuoteInitialFunds(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -431,11 +431,11 @@ func TestQuoteInitialFunds(t *testing.T) { func TestBaseAvailable(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -450,11 +450,11 @@ func TestBaseAvailable(t *testing.T) { func TestQuoteAvailable(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -469,11 +469,11 @@ func TestQuoteAvailable(t *testing.T) { func TestReservePair(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -504,11 +504,11 @@ func TestReservePair(t *testing.T) { func TestReleasePair(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -562,11 +562,11 @@ func TestReleasePair(t *testing.T) { func TestIncreaseAvailablePair(t *testing.T) { t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -711,11 +711,11 @@ func TestMatchesItemCurrency(t *testing.T) { if i.MatchesItemCurrency(nil) { t.Errorf("received '%v' expected '%v'", true, false) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -733,11 +733,11 @@ func TestMatchesExchange(t *testing.T) { if i.MatchesExchange(nil) { t.Errorf("received '%v' expected '%v'", true, false) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero, false) + baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -912,7 +912,7 @@ func TestAddUSDTrackingData(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero, false) + quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -955,7 +955,7 @@ func TestAddUSDTrackingData(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - usdtItem, err := CreateItem(exch, a, currency.USDT, elite, decimal.Zero, false) + usdtItem, err := CreateItem(exch, a, currency.USDT, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index d2d1d08fdb6..9492052e40d 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1485,3 +1485,8 @@ func (b *Base) CalculateTotalCollateral(ctx context.Context, subAccount string, func (b *Base) GetFuturesPositions(context.Context, asset.Item, currency.Pair, time.Time, time.Time) ([]order.Detail, error) { return nil, common.ErrNotYetImplemented } + +// GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair +func (b *Base) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) { + return currency.Code{}, common.ErrNotYetImplemented +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index d9d024e6d76..6db43cc9a8b 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1477,3 +1477,11 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency return resp, nil } + +// GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair +func (f *FTX) GetCollateralCurrencyForContract(a asset.Item, _ currency.Pair) (currency.Code, error) { + if a == asset.Futures { + return currency.USD, nil + } + return currency.Code{}, errCollateralCurrencyNotFound +} diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 182fe48a9fa..40badd45ec6 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -56,6 +56,7 @@ type PNLCalculation interface { // multiple ways of calculating the size of collateral // on an exchange type CollateralManagement interface { + GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) ScaleCollateral(context.Context, string, *CollateralCalculator) (decimal.Decimal, error) CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, collaterals []CollateralCalculator) (*TotalCollateralResponse, error) } From 4ce41957c46a4e38b05d979883b71c6ab4a0ee6e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 21 Jan 2022 15:39:45 +1100 Subject: [PATCH 064/171] Begins the creation of cash and carry strategy --- backtester/backtest/setup.go | 5 - backtester/common/common_types.go | 1 + backtester/config/config_test.go | 34 ++-- ...api-candles.strat => ftx-cash-carry.strat} | 48 +++-- .../eventhandlers/strategies/base/base.go | 24 ++- .../strategies/base/base_types.go | 1 + .../ftxcashandcarry/ftxcashandcarry.go | 126 ++++++++++++ .../ftxcashandcarry_test.go} | 2 +- .../ftxcashandcarry_types.go} | 7 +- .../ftxquarterlyfutures.go | 186 ------------------ .../eventhandlers/strategies/strategies.go | 2 + backtester/eventtypes/event/event.go | 5 + backtester/eventtypes/event/event_types.go | 15 +- backtester/eventtypes/signal/signal_types.go | 6 + .../trackingcurrencies/trackingcurrencies.go | 4 + backtester/main.go | 2 +- 16 files changed, 225 insertions(+), 243 deletions(-) rename backtester/config/examples/{futures-api-candles.strat => ftx-cash-carry.strat} (63%) create mode 100644 backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go rename backtester/eventhandlers/strategies/{ftxquarterlyfutures/ftxquarterlyfutures_test.go => ftxcashandcarry/ftxcashandcarry_test.go} (99%) rename backtester/eventhandlers/strategies/{ftxquarterlyfutures/ftxquarterlyfutures_types.go => ftxcashandcarry/ftxcashandcarry_types.go} (84%) delete mode 100644 backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go index e8445851018..b6949a1b53c 100644 --- a/backtester/backtest/setup.go +++ b/backtester/backtest/setup.go @@ -41,8 +41,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) -var futuresEnabled = true - // NewFromConfig takes a strategy config and configures a backtester variable to run func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, error) { log.Infoln(log.BackTester, "loading config...") @@ -266,9 +264,6 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, return nil, err } case a.IsFutures(): - if !futuresEnabled { - return nil, fmt.Errorf("%w: %v unsupported", errInvalidConfigAsset, a) - } // setup contract items c := funding.CreateFuturesCurrencyCode(b, q) futureItem, err = funding.CreateItem(cfg.CurrencySettings[i].ExchangeName, diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 75071005c13..83027929eed 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -61,6 +61,7 @@ type EventHandler interface { IsEvent() bool GetTime() time.Time Pair() currency.Pair + GetUnderlyingPair() (currency.Pair, error) GetExchange() string GetInterval() kline.Interval GetAssetType() asset.Item diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 075fb4b78f1..d2faa52d1f0 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1198,24 +1198,13 @@ func TestValidate(t *testing.T) { } } -func TestGenerateConfigForFuturesAPICandles(t *testing.T) { +func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { cfg := Config{ - Nickname: "ExampleStrategyFuturesAPICandles", - Goal: "To demonstrate DCA strategy using API candles", + Nickname: "Example Cash and Carry", + Goal: "To demonstrate a cash and carry strategy", StrategySettings: StrategySettings{ - Name: dca, - UseExchangeLevelFunding: true, + Name: "ftx-cash-carry", SimultaneousSignalProcessing: true, - DisableUSDTracking: true, - ExchangeLevelFunding: []ExchangeLevelFunding{ - { - ExchangeName: "ftx", - Asset: asset.Futures.String(), - Currency: "usdt", - InitialFunds: *initialQuoteFunds2, - TransferFee: decimal.Zero, - }, - }, }, CurrencySettings: []CurrencySettings{ { @@ -1234,6 +1223,19 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { MakerFee: makerFee, TakerFee: takerFee, }, + { + ExchangeName: "ftx", + Asset: asset.Spot.String(), + Base: "BTC", + Quote: "USD", + SpotDetails: &SpotDetails{ + InitialQuoteFunds: initialQuoteFunds2, + }, + BuySide: minMax, + SellSide: minMax, + MakerFee: makerFee, + TakerFee: takerFee, + }, }, DataSettings: DataSettings{ Interval: kline.OneDay.Duration(), @@ -1264,7 +1266,7 @@ func TestGenerateConfigForFuturesAPICandles(t *testing.T) { if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(p, "examples", "futures-api-candles.strat"), result, 0770) + err = ioutil.WriteFile(filepath.Join(p, "examples", "ftx-cash-carry.strat"), result, 0770) if err != nil { t.Error(err) } diff --git a/backtester/config/examples/futures-api-candles.strat b/backtester/config/examples/ftx-cash-carry.strat similarity index 63% rename from backtester/config/examples/futures-api-candles.strat rename to backtester/config/examples/ftx-cash-carry.strat index 555d9d96d55..7760f970450 100644 --- a/backtester/config/examples/futures-api-candles.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -1,20 +1,11 @@ { - "nickname": "ExampleStrategyFuturesAPICandles", - "goal": "To demonstrate DCA strategy using API candles", + "nickname": "Example Cash and Carry", + "goal": "To demonstrate a cash and carry strategy", "strategy-settings": { - "name": "dollarcostaverage", - "use-simultaneous-signal-processing": false, - "use-exchange-level-funding": true, - "exchange-level-funding": [ - { - "exchange-name": "ftx", - "asset": "futures", - "currency": "usd", - "initial-funds": "100000", - "transfer-fee": "0" - } - ], - "disable-usd-tracking": true + "name": "ftx-cash-carry", + "use-simultaneous-signal-processing": true, + "use-exchange-level-funding": false, + "disable-usd-tracking": false }, "currency-settings": [ { @@ -48,6 +39,33 @@ "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, "use-exchange-pnl-calculation": false + }, + { + "exchange-name": "ftx", + "asset": "spot", + "base": "BTC", + "quote": "USD", + "spot-details": { + "initial-quote-funds": "100000" + }, + "buy-side": { + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" + }, + "sell-side": { + "minimum-size": "0.005", + "maximum-size": "2", + "maximum-total": "40000" + }, + "min-slippage-percent": "0", + "max-slippage-percent": "0", + "maker-fee-override": "0.001", + "taker-fee-override": "0.002", + "maximum-holdings-ratio": "0", + "skip-candle-volume-fitting": false, + "use-exchange-order-limits": false, + "use-exchange-pnl-calculation": false } ], "data-settings": { diff --git a/backtester/eventhandlers/strategies/base/base.go b/backtester/eventhandlers/strategies/base/base.go index 2ecf958f4e8..8beed7ff653 100644 --- a/backtester/eventhandlers/strategies/base/base.go +++ b/backtester/eventhandlers/strategies/base/base.go @@ -5,6 +5,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" + "github.com/thrasher-corp/gocryptotrader/currency" ) // Strategy is base implementation of the Handler interface @@ -22,15 +23,24 @@ func (s *Strategy) GetBaseData(d data.Handler) (signal.Signal, error) { if latest == nil { return signal.Signal{}, common.ErrNilEvent } + var underlying currency.Pair + if latest.GetAssetType().IsFutures() { + var err error + underlying, err = latest.GetUnderlyingPair() + if err != nil { + return signal.Signal{}, err + } + } return signal.Signal{ Base: event.Base{ - Offset: latest.GetOffset(), - Exchange: latest.GetExchange(), - Time: latest.GetTime(), - CurrencyPair: latest.Pair(), - AssetType: latest.GetAssetType(), - Interval: latest.GetInterval(), - Reason: latest.GetReason(), + Offset: latest.GetOffset(), + Exchange: latest.GetExchange(), + Time: latest.GetTime(), + CurrencyPair: latest.Pair(), + AssetType: latest.GetAssetType(), + Interval: latest.GetInterval(), + Reason: latest.GetReason(), + UnderlyingPair: underlying, }, ClosePrice: latest.GetClosePrice(), HighPrice: latest.GetHighPrice(), diff --git a/backtester/eventhandlers/strategies/base/base_types.go b/backtester/eventhandlers/strategies/base/base_types.go index 21f854ce0d7..2f2e096f195 100644 --- a/backtester/eventhandlers/strategies/base/base_types.go +++ b/backtester/eventhandlers/strategies/base/base_types.go @@ -8,6 +8,7 @@ var ( // ErrSimultaneousProcessingNotSupported used when strategy does not support simultaneous processing // but start config is set to use it ErrSimultaneousProcessingNotSupported = errors.New("does not support simultaneous processing and could not be loaded") + ErrSimultaneousProcessingOnly = errors.New("this strategy only supports simultaneous processing") // ErrStrategyNotFound used when strategy specified in start config does not exist ErrStrategyNotFound = errors.New("not found. Please ensure the strategy-settings field 'name' is spelled properly in your .start config") // ErrInvalidCustomSettings used when bad custom settings are found in the start config diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go new file mode 100644 index 00000000000..619ca490578 --- /dev/null +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -0,0 +1,126 @@ +package ftxcashandcarry + +import ( + "errors" + "fmt" + "strings" + + "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" + "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +// Name returns the name of the strategy +func (s *Strategy) Name() string { + return Name +} + +// Description provides a nice overview of the strategy +// be it definition of terms or to highlight its purpose +func (s *Strategy) Description() string { + return description +} + +// OnSignal handles a data event and returns what action the strategy believes should occur +// For rsi, this means returning a buy signal when rsi is at or below a certain level, and a +// sell signal when it is at or above a certain level +func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfolio.Handler) (signal.Event, error) { + return nil, base.ErrSimultaneousProcessingOnly +} + +// SupportsSimultaneousProcessing highlights whether the strategy can handle multiple currency calculation +// There is nothing actually stopping this strategy from considering multiple currencies at once +// but for demonstration purposes, this strategy does not +func (s *Strategy) SupportsSimultaneousProcessing() bool { + return true +} + +type cashCarrySignals struct { + spotSignal data.Handler + futureSignal data.Handler +} + +var errNotSetup = errors.New("sent incomplete signals") + +func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { + var response = make(map[currency.Pair]cashCarrySignals) + for i := range d { + l := d[i].Latest() + if !strings.EqualFold(l.GetExchange(), exchangeName) { + return nil, fmt.Errorf("%w, received '%v'", errOnlyFTXSupported, l.GetExchange()) + } + a := l.GetAssetType() + switch { + case a == asset.Spot: + entry := response[l.Pair()] + entry.spotSignal = d[i] + response[l.Pair()] = entry + case a.IsFutures(): + u, err := l.GetUnderlyingPair() + if err != nil { + return nil, err + } + entry := response[u] + entry.futureSignal = d[i] + response[l.Pair()] = entry + default: + return nil, errFuturesOnly + } + } + // validate that each set of signals is matched + for _, v := range response { + if v.futureSignal == nil { + return nil, errNotSetup + + } + if v.spotSignal == nil { + return nil, errNotSetup + } + } + + return response, nil +} + +// OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility +// in allowing a strategy to only place an order for X currency if Y currency's price is Z +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { + var response []signal.Event + sortedSignals, err := sortSignals(d) + if err != nil { + return nil, err + } + for _, v := range sortedSignals { + spotSignal, err := s.GetBaseData(v.spotSignal) + if err != nil { + return nil, err + } + futuresSignal, err := s.GetBaseData(v.futureSignal) + if err != nil { + return nil, err + } + spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) + futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) + // first the spot purchase + spotSignal.SetDirection(order.Buy) + // second the futures purchase, using the newly acquired asset + // as collateral to short + futuresSignal.SetDirection(order.Sell) + futuresSignal.RequiresCollateral = true + futuresSignal.CollateralCurrency = futuresSignal.UnderlyingPair.Base + response = append(response, &spotSignal, &futuresSignal) + } + return response, nil +} + +// SetCustomSettings not required for DCA +func (s *Strategy) SetCustomSettings(_ map[string]interface{}) error { + return base.ErrCustomSettingsUnsupported +} + +// SetDefaults not required for DCA +func (s *Strategy) SetDefaults() {} diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go similarity index 99% rename from backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go rename to backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 18244cba84e..c32dd79f57a 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -1,4 +1,4 @@ -package ftxquarterlyfutures +package ftxcashandcarry import ( "errors" diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go similarity index 84% rename from backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go rename to backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go index 3413bb1674b..306def1c6ec 100644 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures_types.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go @@ -1,4 +1,4 @@ -package ftxquarterlyfutures +package ftxcashandcarry import ( "errors" @@ -9,10 +9,7 @@ import ( const ( // Name is the strategy name - Name = "futures-rsi" - rsiPeriodKey = "rsi-period" - rsiLowKey = "rsi-low" - rsiHighKey = "rsi-high" + Name = "ftx-cash-carry" description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` exchangeName = "ftx" ) diff --git a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go b/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go deleted file mode 100644 index cb3b649fcd9..00000000000 --- a/backtester/eventhandlers/strategies/ftxquarterlyfutures/ftxquarterlyfutures.go +++ /dev/null @@ -1,186 +0,0 @@ -package ftxquarterlyfutures - -import ( - "fmt" - "strings" - "time" - - "github.com/shopspring/decimal" - "github.com/thrasher-corp/gct-ta/indicators" - "github.com/thrasher-corp/gocryptotrader/backtester/common" - "github.com/thrasher-corp/gocryptotrader/backtester/data" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" - "github.com/thrasher-corp/gocryptotrader/backtester/funding" - gctcommon "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" -) - -// Name returns the name of the strategy -func (s *Strategy) Name() string { - return Name -} - -// Description provides a nice overview of the strategy -// be it definition of terms or to highlight its purpose -func (s *Strategy) Description() string { - return description -} - -// OnSignal handles a data event and returns what action the strategy believes should occur -// For rsi, this means returning a buy signal when rsi is at or below a certain level, and a -// sell signal when it is at or above a certain level -func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfolio.Handler) (signal.Event, error) { - if d == nil { - return nil, common.ErrNilEvent - } - latest := d.Latest() - if !latest.GetAssetType().IsFutures() { - return nil, errFuturesOnly - } - if !strings.EqualFold(latest.GetExchange(), exchangeName) { - return nil, errOnlyFTXSupported - } - - es, err := s.GetBaseData(d) - if err != nil { - return nil, err - } - es.SetPrice(d.Latest().GetClosePrice()) - - if offset := d.Offset(); offset <= int(s.rsiPeriod.IntPart()) { - es.AppendReason("Not enough data for signal generation") - es.SetDirection(common.DoNothing) - return &es, nil - } - - dataRange := d.StreamClose() - var massagedData []float64 - massagedData, err = s.massageMissingData(dataRange, es.GetTime()) - if err != nil { - return nil, err - } - rsi := indicators.RSI(massagedData, int(s.rsiPeriod.IntPart())) - latestRSIValue := decimal.NewFromFloat(rsi[len(rsi)-1]) - if !d.HasDataAtTime(d.Latest().GetTime()) { - es.SetDirection(common.MissingData) - es.AppendReason(fmt.Sprintf("missing data at %v, cannot perform any actions. RSI %v", d.Latest().GetTime(), latestRSIValue)) - return &es, nil - } - es.AppendReason(fmt.Sprintf("RSI at %v", latestRSIValue)) - - positions, err := p.GetPositions(&es) - if err != nil { - return nil, err - } - - if len(positions) > 0 { - latestPosition := positions[len(positions)-1] - if latestPosition.Status == order.Open && latestPosition.Exposure.GreaterThan(decimal.Zero) { - // close out the position - switch latestPosition.Direction { - case order.Short: - if latestRSIValue.LessThanOrEqual(s.rsiLow) { - // close out your positions! - es.SetDirection(order.Long) - es.SetAmount(latestPosition.Exposure) - return &es, nil - } - case order.Long: - if latestRSIValue.GreaterThanOrEqual(s.rsiHigh) { - // close out your positions! - es.SetDirection(order.Short) - es.SetAmount(latestPosition.Exposure) - return &es, nil - } - } - } - } - switch { - case latestRSIValue.GreaterThanOrEqual(s.rsiHigh): - es.SetDirection(order.Short) - case latestRSIValue.LessThanOrEqual(s.rsiLow): - es.SetDirection(order.Long) - default: - es.SetDirection(common.DoNothing) - } - - return &es, nil -} - -// SupportsSimultaneousProcessing highlights whether the strategy can handle multiple currency calculation -// There is nothing actually stopping this strategy from considering multiple currencies at once -// but for demonstration purposes, this strategy does not -func (s *Strategy) SupportsSimultaneousProcessing() bool { - return false -} - -// OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility -// in allowing a strategy to only place an order for X currency if Y currency's price is Z -func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { - return nil, base.ErrSimultaneousProcessingNotSupported -} - -// SetCustomSettings allows a user to modify the RSI limits in their config -func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) error { - for k, v := range customSettings { - switch k { - case rsiHighKey: - rsiHigh, ok := v.(float64) - if !ok || rsiHigh <= 0 { - return fmt.Errorf("%w provided rsi-high value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.rsiHigh = decimal.NewFromFloat(rsiHigh) - case rsiLowKey: - rsiLow, ok := v.(float64) - if !ok || rsiLow <= 0 { - return fmt.Errorf("%w provided rsi-low value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.rsiLow = decimal.NewFromFloat(rsiLow) - case rsiPeriodKey: - rsiPeriod, ok := v.(float64) - if !ok || rsiPeriod <= 0 { - return fmt.Errorf("%w provided rsi-period value could not be parsed: %v", base.ErrInvalidCustomSettings, v) - } - s.rsiPeriod = decimal.NewFromFloat(rsiPeriod) - default: - return fmt.Errorf("%w unrecognised custom setting key %v with value %v. Cannot apply", base.ErrInvalidCustomSettings, k, v) - } - } - - return nil -} - -// SetDefaults sets the custom settings to their default values -func (s *Strategy) SetDefaults() { - s.rsiHigh = decimal.NewFromInt(70) - s.rsiLow = decimal.NewFromInt(30) - s.rsiPeriod = decimal.NewFromInt(14) -} - -// massageMissingData will replace missing data with the previous candle's data -// this will ensure that RSI can be calculated correctly -// the decision to handle missing data occurs at the strategy level, not all strategies -// may wish to modify data -func (s *Strategy) massageMissingData(data []decimal.Decimal, t time.Time) ([]float64, error) { - var resp []float64 - var missingDataStreak int64 - for i := range data { - if data[i].IsZero() && i > int(s.rsiPeriod.IntPart()) { - data[i] = data[i-1] - missingDataStreak++ - } else { - missingDataStreak = 0 - } - if missingDataStreak >= s.rsiPeriod.IntPart() { - return nil, fmt.Errorf("missing data exceeds RSI period length of %v at %s and will distort results. %w", - s.rsiPeriod, - t.Format(gctcommon.SimpleTimeFormat), - base.ErrTooMuchBadData) - } - d, _ := data[i].Float64() - resp = append(resp, d) - } - return resp, nil -} diff --git a/backtester/eventhandlers/strategies/strategies.go b/backtester/eventhandlers/strategies/strategies.go index 99982a734ab..e5e8d50c867 100644 --- a/backtester/eventhandlers/strategies/strategies.go +++ b/backtester/eventhandlers/strategies/strategies.go @@ -6,6 +6,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/dollarcostaverage" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/ftxcashandcarry" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/rsi" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/top2bottom2" ) @@ -38,5 +39,6 @@ func GetStrategies() []Handler { new(dollarcostaverage.Strategy), new(rsi.Strategy), new(top2bottom2.Strategy), + new(ftxcashandcarry.Strategy), } } diff --git a/backtester/eventtypes/event/event.go b/backtester/eventtypes/event/event.go index ad2660af98b..64d096ac77c 100644 --- a/backtester/eventtypes/event/event.go +++ b/backtester/eventtypes/event/event.go @@ -34,6 +34,11 @@ func (b *Base) Pair() currency.Pair { return b.CurrencyPair } +// Pair returns the currency pair +func (b *Base) GetUnderlyingPair() (currency.Pair, error) { + return b.UnderlyingPair, nil +} + // GetExchange returns the exchange func (b *Base) GetExchange() string { return strings.ToLower(b.Exchange) diff --git a/backtester/eventtypes/event/event_types.go b/backtester/eventtypes/event/event_types.go index 9c0af60d53b..db002287843 100644 --- a/backtester/eventtypes/event/event_types.go +++ b/backtester/eventtypes/event/event_types.go @@ -12,11 +12,12 @@ import ( // Data, fill, order events all contain the base event and store important and // consistent information type Base struct { - Offset int64 `json:"-"` - Exchange string `json:"exchange"` - Time time.Time `json:"timestamp"` - Interval kline.Interval `json:"interval-size"` - CurrencyPair currency.Pair `json:"pair"` - AssetType asset.Item `json:"asset"` - Reason string `json:"reason"` + Offset int64 `json:"-"` + Exchange string `json:"exchange"` + Time time.Time `json:"timestamp"` + Interval kline.Interval `json:"interval-size"` + CurrencyPair currency.Pair `json:"pair"` + UnderlyingPair currency.Pair `json:"underlying"` + AssetType asset.Item `json:"asset"` + Reason string `json:"reason"` } diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index 81fe7e5806a..087dadb761e 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -4,6 +4,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -42,4 +43,9 @@ type Signal struct { // the order will not be placed Amount decimal.Decimal Direction order.Side + // RequiresCollateral ensures that an order can only be placed + // if there is corresponding collateral in the selected currency + // this enabled cash and carry strategies for example + RequiresCollateral bool + CollateralCurrency currency.Code } diff --git a/backtester/funding/trackingcurrencies/trackingcurrencies.go b/backtester/funding/trackingcurrencies/trackingcurrencies.go index 67460c4ccb2..121a7b2f38e 100644 --- a/backtester/funding/trackingcurrencies/trackingcurrencies.go +++ b/backtester/funding/trackingcurrencies/trackingcurrencies.go @@ -75,6 +75,10 @@ func CreateUSDTrackingPairs(tp []TrackingPair, em *engine.ExchangeManager) ([]Tr if err != nil { return nil, err } + if a.IsFutures() { + // futures matches to spot, not like this + continue + } pairs := b.CurrencyPairs.Pairs[a] basePair, quotePair, err := findMatchingUSDPairs(pair, pairs) if err != nil { diff --git a/backtester/main.go b/backtester/main.go index 14929db30b8..9dda3a0c571 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -28,7 +28,7 @@ func main() { wd, "config", "examples", - "futures-api-candles.strat"), + "ftx-cash-carry.strat"), "the config containing strategy params") flag.StringVar( &templatePath, From 76bf6612bde9aa3504df95d1b29be0b32d4c7dc7 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 24 Jan 2022 15:49:18 +1100 Subject: [PATCH 065/171] Adds underlying pair, adds filldependentevent for futures --- backtester/backtest/backtest.go | 4 + backtester/backtest/setup.go | 195 ++++++++++-------- backtester/common/common_types.go | 2 +- backtester/config/config_test.go | 23 ++- .../config/examples/ftx-cash-carry.strat | 15 +- backtester/data/kline/kline.go | 13 +- .../ftxcashandcarry/ftxcashandcarry.go | 53 +++-- backtester/eventtypes/event/event.go | 5 - backtester/eventtypes/fill/fill_types.go | 4 + backtester/eventtypes/kline/kline.go | 6 + backtester/eventtypes/order/order_types.go | 22 +- backtester/eventtypes/signal/signal.go | 8 + backtester/eventtypes/signal/signal_types.go | 6 +- backtester/funding/funding.go | 27 ++- exchanges/kline/kline_types.go | 1 + 15 files changed, 226 insertions(+), 158 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 33a39a73a46..b2fc479eee1 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -283,6 +283,10 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Errorf(log.BackTester, "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } + if fde := ev.GetFillDependentEvent(); fde != nil { + // some events can only be triggered on a successful fill event + bt.EventQueue.AppendEvent(fde) + } err = bt.Statistic.SetEventForOffset(t) if err != nil { diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go index b6949a1b53c..b3211a29860 100644 --- a/backtester/backtest/setup.go +++ b/backtester/backtest/setup.go @@ -447,105 +447,108 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange return resp, err } - if !cfg.CurrencySettings[i].USDTrackingPair { - bt.Datas.SetDataForCurrency(exchangeName, a, pair, klineData) - var makerFee, takerFee decimal.Decimal - if cfg.CurrencySettings[i].MakerFee.GreaterThan(decimal.Zero) { - makerFee = cfg.CurrencySettings[i].MakerFee - } - if cfg.CurrencySettings[i].TakerFee.GreaterThan(decimal.Zero) { - takerFee = cfg.CurrencySettings[i].TakerFee - } - if makerFee.IsZero() || takerFee.IsZero() { - var apiMakerFee, apiTakerFee decimal.Decimal - apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair) - if makerFee.IsZero() { - makerFee = apiMakerFee - } - if takerFee.IsZero() { - takerFee = apiTakerFee - } - } + if cfg.CurrencySettings[i].USDTrackingPair { + continue + } - if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(log.BackTester, "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", - cfg.CurrencySettings[i].MaximumSlippagePercent, - slippage.DefaultMaximumSlippagePercent) - cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent - } - if cfg.CurrencySettings[i].MaximumSlippagePercent.IsZero() { - cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent - } - if cfg.CurrencySettings[i].MinimumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(log.BackTester, "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", - cfg.CurrencySettings[i].MinimumSlippagePercent, - slippage.DefaultMinimumSlippagePercent) - cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent - } - if cfg.CurrencySettings[i].MinimumSlippagePercent.IsZero() { - cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent + bt.Datas.SetDataForCurrency(exchangeName, a, pair, klineData) + + var makerFee, takerFee decimal.Decimal + if cfg.CurrencySettings[i].MakerFee.GreaterThan(decimal.Zero) { + makerFee = cfg.CurrencySettings[i].MakerFee + } + if cfg.CurrencySettings[i].TakerFee.GreaterThan(decimal.Zero) { + takerFee = cfg.CurrencySettings[i].TakerFee + } + if makerFee.IsZero() || takerFee.IsZero() { + var apiMakerFee, apiTakerFee decimal.Decimal + apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair) + if makerFee.IsZero() { + makerFee = apiMakerFee } - if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(cfg.CurrencySettings[i].MinimumSlippagePercent) { - cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + if takerFee.IsZero() { + takerFee = apiTakerFee } + } - realOrders := false - if cfg.DataSettings.LiveData != nil { - realOrders = cfg.DataSettings.LiveData.RealOrders - } + if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(decimal.Zero) { + log.Warnf(log.BackTester, "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", + cfg.CurrencySettings[i].MaximumSlippagePercent, + slippage.DefaultMaximumSlippagePercent) + cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + } + if cfg.CurrencySettings[i].MaximumSlippagePercent.IsZero() { + cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + } + if cfg.CurrencySettings[i].MinimumSlippagePercent.LessThan(decimal.Zero) { + log.Warnf(log.BackTester, "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", + cfg.CurrencySettings[i].MinimumSlippagePercent, + slippage.DefaultMinimumSlippagePercent) + cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent + } + if cfg.CurrencySettings[i].MinimumSlippagePercent.IsZero() { + cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent + } + if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(cfg.CurrencySettings[i].MinimumSlippagePercent) { + cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent + } - buyRule := exchange.MinMax{ - MinimumSize: cfg.CurrencySettings[i].BuySide.MinimumSize, - MaximumSize: cfg.CurrencySettings[i].BuySide.MaximumSize, - MaximumTotal: cfg.CurrencySettings[i].BuySide.MaximumTotal, - } - sellRule := exchange.MinMax{ - MinimumSize: cfg.CurrencySettings[i].SellSide.MinimumSize, - MaximumSize: cfg.CurrencySettings[i].SellSide.MaximumSize, - MaximumTotal: cfg.CurrencySettings[i].SellSide.MaximumTotal, - } + realOrders := false + if cfg.DataSettings.LiveData != nil { + realOrders = cfg.DataSettings.LiveData.RealOrders + } - limits, err := exch.GetOrderExecutionLimits(a, pair) - if err != nil && !errors.Is(err, gctorder.ErrExchangeLimitNotLoaded) { - return resp, err - } + buyRule := exchange.MinMax{ + MinimumSize: cfg.CurrencySettings[i].BuySide.MinimumSize, + MaximumSize: cfg.CurrencySettings[i].BuySide.MaximumSize, + MaximumTotal: cfg.CurrencySettings[i].BuySide.MaximumTotal, + } + sellRule := exchange.MinMax{ + MinimumSize: cfg.CurrencySettings[i].SellSide.MinimumSize, + MaximumSize: cfg.CurrencySettings[i].SellSide.MaximumSize, + MaximumTotal: cfg.CurrencySettings[i].SellSide.MaximumTotal, + } - if limits != nil { - if !cfg.CurrencySettings[i].CanUseExchangeLimits { - log.Warnf(log.BackTester, "exchange %s order execution limits supported but disabled for %s %s, live results may differ", - cfg.CurrencySettings[i].ExchangeName, - pair, - a) - cfg.CurrencySettings[i].ShowExchangeOrderLimitWarning = true - } + limits, err := exch.GetOrderExecutionLimits(a, pair) + if err != nil && !errors.Is(err, gctorder.ErrExchangeLimitNotLoaded) { + return resp, err + } + + if limits != nil { + if !cfg.CurrencySettings[i].CanUseExchangeLimits { + log.Warnf(log.BackTester, "exchange %s order execution limits supported but disabled for %s %s, live results may differ", + cfg.CurrencySettings[i].ExchangeName, + pair, + a) + cfg.CurrencySettings[i].ShowExchangeOrderLimitWarning = true } - var lev exchange.Leverage - if cfg.CurrencySettings[i].FuturesDetails != nil { - lev = exchange.Leverage{ - CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, - MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, - MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, - } + } + var lev exchange.Leverage + if cfg.CurrencySettings[i].FuturesDetails != nil { + lev = exchange.Leverage{ + CanUseLeverage: cfg.CurrencySettings[i].FuturesDetails.Leverage.CanUseLeverage, + MaximumLeverageRate: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate, + MaximumOrdersWithLeverageRatio: cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio, } - resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ - Exchange: exch, - MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, - MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, - Pair: pair, - Asset: a, - ExchangeFee: takerFee, - MakerFee: takerFee, - TakerFee: makerFee, - UseRealOrders: realOrders, - BuySide: buyRule, - SellSide: sellRule, - Leverage: lev, - Limits: limits, - SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, - CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, - UseExchangePNLCalculation: cfg.CurrencySettings[i].UseExchangePNLCalculation, - }) } + resp.CurrencySettings = append(resp.CurrencySettings, exchange.Settings{ + Exchange: exch, + MinimumSlippageRate: cfg.CurrencySettings[i].MinimumSlippagePercent, + MaximumSlippageRate: cfg.CurrencySettings[i].MaximumSlippagePercent, + Pair: pair, + Asset: a, + ExchangeFee: takerFee, + MakerFee: takerFee, + TakerFee: makerFee, + UseRealOrders: realOrders, + BuySide: buyRule, + SellSide: sellRule, + Leverage: lev, + Limits: limits, + SkipCandleVolumeFitting: cfg.CurrencySettings[i].SkipCandleVolumeFitting, + CanUseExchangeLimits: cfg.CurrencySettings[i].CanUseExchangeLimits, + UseExchangePNLCalculation: cfg.CurrencySettings[i].UseExchangePNLCalculation, + }) } return resp, nil @@ -751,6 +754,20 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, return nil, fmt.Errorf("processing error, response returned nil") } + if a.IsFutures() { + // returning the collateral currency along with using the + // fPair base creates a pair that links the futures contract to + // is underlying pair + // eg BTC-PERP on FTX has a collateral currency of USD + // taking the BTC base and USD as quote, allows linking + // BTC-USD and BTC-PERP + curr, err := exch.GetCollateralCurrencyForContract(a, fPair) + if err != nil { + return resp, err + } + resp.Item.UnderlyingPair = currency.NewPair(fPair.Base, curr) + } + err = b.ValidateKline(fPair, a, resp.Item.Interval) if err != nil { if dataType != common.DataTrade || !strings.EqualFold(err.Error(), "interval not supported") { diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 83027929eed..2923264c99a 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -61,7 +61,6 @@ type EventHandler interface { IsEvent() bool GetTime() time.Time Pair() currency.Pair - GetUnderlyingPair() (currency.Pair, error) GetExchange() string GetInterval() kline.Interval GetAssetType() asset.Item @@ -72,6 +71,7 @@ type EventHandler interface { // DataEventHandler interface used for loading and interacting with Data type DataEventHandler interface { EventHandler + GetUnderlyingPair() (currency.Pair, error) GetClosePrice() decimal.Decimal GetHighPrice() decimal.Decimal GetLowPrice() decimal.Decimal diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index d2faa52d1f0..dee2bebb384 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1205,7 +1205,18 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { StrategySettings: StrategySettings{ Name: "ftx-cash-carry", SimultaneousSignalProcessing: true, + UseExchangeLevelFunding: true, + ExchangeLevelFunding: []ExchangeLevelFunding{ + { + ExchangeName: "ftx", + Asset: "spot", + Currency: "USD", + InitialFunds: *initialQuoteFunds2, + TransferFee: decimal.Decimal{}, + }, + }, }, + CurrencySettings: []CurrencySettings{ { ExchangeName: "ftx", @@ -1228,13 +1239,11 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { Asset: asset.Spot.String(), Base: "BTC", Quote: "USD", - SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, - }, - BuySide: minMax, - SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + SpotDetails: &SpotDetails{}, + BuySide: minMax, + SellSide: minMax, + MakerFee: makerFee, + TakerFee: takerFee, }, }, DataSettings: DataSettings{ diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 7760f970450..5d701a96ace 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -4,7 +4,16 @@ "strategy-settings": { "name": "ftx-cash-carry", "use-simultaneous-signal-processing": true, - "use-exchange-level-funding": false, + "use-exchange-level-funding": true, + "exchange-level-funding": [ + { + "exchange-name": "ftx", + "asset": "spot", + "currency": "USD", + "initial-funds": "100000", + "transfer-fee": "0" + } + ], "disable-usd-tracking": false }, "currency-settings": [ @@ -45,9 +54,7 @@ "asset": "spot", "base": "BTC", "quote": "USD", - "spot-details": { - "initial-quote-funds": "100000" - }, + "spot-details": {}, "buy-side": { "minimum-size": "0.005", "maximum-size": "2", diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index 7cde8784a14..1a76f39f07b 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -31,12 +31,13 @@ func (d *DataFromKline) Load() error { for i := range d.Item.Candles { klinerino := &kline.Kline{ Base: event.Base{ - Offset: int64(i + 1), - Exchange: d.Item.Exchange, - Time: d.Item.Candles[i].Time, - Interval: d.Item.Interval, - CurrencyPair: d.Item.Pair, - AssetType: d.Item.Asset, + Offset: int64(i + 1), + Exchange: d.Item.Exchange, + Time: d.Item.Candles[i].Time, + Interval: d.Item.Interval, + CurrencyPair: d.Item.Pair, + AssetType: d.Item.Asset, + UnderlyingPair: d.Item.UnderlyingPair, }, Open: decimal.NewFromFloat(d.Item.Candles[i].Open), High: decimal.NewFromFloat(d.Item.Candles[i].High), diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 619ca490578..35357ea35c8 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -29,7 +29,7 @@ func (s *Strategy) Description() string { // OnSignal handles a data event and returns what action the strategy believes should occur // For rsi, this means returning a buy signal when rsi is at or below a certain level, and a // sell signal when it is at or above a certain level -func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, p portfolio.Handler) (signal.Event, error) { +func (s *Strategy) OnSignal(data.Handler, funding.IFundTransferer, portfolio.Handler) (signal.Event, error) { return nil, base.ErrSimultaneousProcessingOnly } @@ -47,7 +47,7 @@ type cashCarrySignals struct { var errNotSetup = errors.New("sent incomplete signals") -func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { +func sortSignals(d []data.Handler, f funding.IFundTransferer) (map[currency.Pair]cashCarrySignals, error) { var response = make(map[currency.Pair]cashCarrySignals) for i := range d { l := d[i].Latest() @@ -57,17 +57,18 @@ func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { a := l.GetAssetType() switch { case a == asset.Spot: - entry := response[l.Pair()] + + entry := response[l.Pair().Format("", false)] entry.spotSignal = d[i] - response[l.Pair()] = entry + response[l.Pair().Format("", false)] = entry case a.IsFutures(): u, err := l.GetUnderlyingPair() if err != nil { return nil, err } - entry := response[u] + entry := response[u.Format("", false)] entry.futureSignal = d[i] - response[l.Pair()] = entry + response[u.Format("", false)] = entry default: return nil, errFuturesOnly } @@ -88,31 +89,39 @@ func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z -func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransferer, p portfolio.Handler) ([]signal.Event, error) { var response []signal.Event - sortedSignals, err := sortSignals(d) + sortedSignals, err := sortSignals(d, f) if err != nil { return nil, err } + for _, v := range sortedSignals { - spotSignal, err := s.GetBaseData(v.spotSignal) + pos, err := p.GetPositions(v.futureSignal.Latest()) if err != nil { return nil, err } - futuresSignal, err := s.GetBaseData(v.futureSignal) - if err != nil { - return nil, err + if len(pos) == 0 { + spotSignal, err := s.GetBaseData(v.spotSignal) + if err != nil { + return nil, err + } + futuresSignal, err := s.GetBaseData(v.futureSignal) + if err != nil { + return nil, err + } + spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) + spotSignal.AppendReason(fmt.Sprintf("signalling purchase of %v", spotSignal.Pair())) + futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) + // first the spot purchase + spotSignal.SetDirection(order.Buy) + // second the futures purchase, using the newly acquired asset + // as collateral to short + futuresSignal.SetDirection(order.Short) + spotSignal.FillDependentEvent = &futuresSignal + spotSignal.AppendReason(fmt.Sprintf("signalling shorting %v", futuresSignal.Pair())) + response = append(response, &spotSignal) } - spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) - futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) - // first the spot purchase - spotSignal.SetDirection(order.Buy) - // second the futures purchase, using the newly acquired asset - // as collateral to short - futuresSignal.SetDirection(order.Sell) - futuresSignal.RequiresCollateral = true - futuresSignal.CollateralCurrency = futuresSignal.UnderlyingPair.Base - response = append(response, &spotSignal, &futuresSignal) } return response, nil } diff --git a/backtester/eventtypes/event/event.go b/backtester/eventtypes/event/event.go index 64d096ac77c..ad2660af98b 100644 --- a/backtester/eventtypes/event/event.go +++ b/backtester/eventtypes/event/event.go @@ -34,11 +34,6 @@ func (b *Base) Pair() currency.Pair { return b.CurrencyPair } -// Pair returns the currency pair -func (b *Base) GetUnderlyingPair() (currency.Pair, error) { - return b.UnderlyingPair, nil -} - // GetExchange returns the exchange func (b *Base) GetExchange() string { return strings.ToLower(b.Exchange) diff --git a/backtester/eventtypes/fill/fill_types.go b/backtester/eventtypes/fill/fill_types.go index f172357a267..cf242e8d8b1 100644 --- a/backtester/eventtypes/fill/fill_types.go +++ b/backtester/eventtypes/fill/fill_types.go @@ -4,6 +4,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -19,6 +20,7 @@ type Fill struct { ExchangeFee decimal.Decimal `json:"exchange-fee"` Slippage decimal.Decimal `json:"slippage"` Order *order.Detail `json:"-"` + FillDependentEvent *signal.Event } // Event holds all functions required to handle a fill event @@ -36,4 +38,6 @@ type Event interface { GetExchangeFee() decimal.Decimal SetExchangeFee(decimal.Decimal) GetOrder() *order.Detail + HasFillDependentEvent() bool + GetFillDependentEvent() signal.Event } diff --git a/backtester/eventtypes/kline/kline.go b/backtester/eventtypes/kline/kline.go index 82efa97b9dd..46c04dde861 100644 --- a/backtester/eventtypes/kline/kline.go +++ b/backtester/eventtypes/kline/kline.go @@ -2,6 +2,7 @@ package kline import ( "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" ) // GetClosePrice returns the closing price of a kline @@ -23,3 +24,8 @@ func (k *Kline) GetLowPrice() decimal.Decimal { func (k *Kline) GetOpenPrice() decimal.Decimal { return k.Open } + +// GetUnderlyingPair returns the open price of a kline +func (k *Kline) GetUnderlyingPair() (currency.Pair, error) { + return k.UnderlyingPair, nil +} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index 4f7c6aff3ff..133d4cb899d 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -4,22 +4,24 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) // Order contains all details for an order event type Order struct { event.Base - ID string - Direction order.Side - Status order.Status - Price decimal.Decimal - Amount decimal.Decimal - OrderType order.Type - Leverage decimal.Decimal - AllocatedFunds decimal.Decimal - BuyLimit decimal.Decimal - SellLimit decimal.Decimal + ID string + Direction order.Side + Status order.Status + Price decimal.Decimal + Amount decimal.Decimal + OrderType order.Type + Leverage decimal.Decimal + AllocatedFunds decimal.Decimal + BuyLimit decimal.Decimal + SellLimit decimal.Decimal + FillDependentEvent *signal.Event } // Event inherits common event interfaces along with extra functions related to handling orders diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index a52f0aff724..3269647766b 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -65,3 +65,11 @@ func (s *Signal) GetAmount() decimal.Decimal { func (s *Signal) SetAmount(d decimal.Decimal) { s.Amount = d } + +// GetUnderlyingPair returns the underlaying currency pair +func (s *Signal) GetUnderlyingPair() (currency.Pair, error) { + if !s.AssetType.IsFutures() { + return s.CurrencyPair, order.ErrNotFutureAsset + } + return s.UnderlyingPair, nil +} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index 087dadb761e..a3c2fdf3982 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -4,7 +4,6 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" - "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -43,9 +42,8 @@ type Signal struct { // the order will not be placed Amount decimal.Decimal Direction order.Side - // RequiresCollateral ensures that an order can only be placed + // FillDependentEvent ensures that an order can only be placed // if there is corresponding collateral in the selected currency // this enabled cash and carry strategies for example - RequiresCollateral bool - CollateralCurrency currency.Code + FillDependentEvent *Signal } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index c48525c5666..625e02762bf 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -144,6 +144,9 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { quoteSet := false var basePairedWith currency.Code for i := range f.items { + if f.items[i].asset.IsFutures() { + return nil + } if baseSet && quoteSet { return nil } @@ -374,14 +377,16 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { var resp Pair var collat Collateral - for i := range f.items { - if a.IsFutures() { + if a.IsFutures() { + for i := range f.items { if f.items[i].MatchesCurrency(currency.NewCode(p.String())) { collat.Contract = f.items[i] collat.Collateral = f.items[i].pairedWith return &collat, nil } - } else { + } + } else { + for i := range f.items { if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { resp.Base = f.items[i] continue @@ -389,15 +394,17 @@ func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pai if f.items[i].BasicEqual(exch, a, p.Quote, p.Base) { resp.Quote = f.items[i] } - if resp.Base == nil { - return nil, fmt.Errorf("base %w", ErrFundsNotFound) - } - if resp.Quote == nil { - return nil, fmt.Errorf("quote %w", ErrFundsNotFound) - } - return &resp, nil } + if resp.Base == nil { + return nil, fmt.Errorf("base %v %w", p.Base, ErrFundsNotFound) + } + if resp.Quote == nil { + return nil, fmt.Errorf("quote %v %w", p.Quote, ErrFundsNotFound) + } + return &resp, nil + } + return nil, fmt.Errorf("%v %v %v %w", exch, a, p, ErrFundsNotFound) } diff --git a/exchanges/kline/kline_types.go b/exchanges/kline/kline_types.go index d2d10c4b8f8..35ab1c611b6 100644 --- a/exchanges/kline/kline_types.go +++ b/exchanges/kline/kline_types.go @@ -82,6 +82,7 @@ var ( type Item struct { Exchange string Pair currency.Pair + UnderlyingPair currency.Pair Asset asset.Item Interval Interval Candles []Candle From 7c5d227ac4f685db5fb29aede1da6a8e6db9f3bb Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 25 Jan 2022 15:29:45 +1100 Subject: [PATCH 066/171] Completes fill prerequsite event implementation. Can't short though --- backtester/backtest/backtest.go | 21 ++-- backtester/config/config_test.go | 8 +- .../config/examples/ftx-cash-carry.strat | 38 +++--- backtester/eventhandlers/exchange/exchange.go | 9 +- .../eventhandlers/exchange/exchange_types.go | 2 +- .../eventhandlers/portfolio/portfolio.go | 114 +++++++++++------- .../portfolio/portfolio_types.go | 4 +- .../ftxcashandcarry/ftxcashandcarry.go | 13 +- backtester/eventtypes/fill/fill.go | 7 ++ backtester/eventtypes/fill/fill_types.go | 3 +- backtester/eventtypes/order/order.go | 7 ++ backtester/eventtypes/order/order_types.go | 3 +- backtester/eventtypes/signal/signal.go | 16 +++ backtester/eventtypes/signal/signal_types.go | 10 ++ backtester/funding/funding.go | 25 ++++ backtester/funding/funding_types.go | 21 ++++ 16 files changed, 211 insertions(+), 90 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index b2fc479eee1..febe239b856 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -97,6 +97,9 @@ dataLoadingIssue: // handle event will process events and add further events to the queue if they // are required func (bt *BackTest) handleEvent(ev common.EventHandler) error { + if ev == nil { + return fmt.Errorf("cannot handle event %w", errNilData) + } funds, err := bt.Funding.GetFundingForEvent(ev) if err != nil { return err @@ -118,13 +121,13 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { bt.Funding.CreateSnapshot(ev.GetTime()) return nil case signal.Event: - bt.processSignalEvent(eType, funds.FundReserver()) + bt.processSignalEvent(eType) case order.Event: bt.processOrderEvent(eType, funds.FundReleaser()) case fill.Event: bt.processFillEvent(eType, funds.FundReleaser()) default: - return fmt.Errorf("%w %v received, could not process", + return fmt.Errorf("%w %T received, could not process", errUnhandledDatatype, ev) } @@ -240,14 +243,14 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } // processSignalEvent receives an event from the strategy for processing under the portfolio -func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) { +func (bt *BackTest) processSignalEvent(ev signal.Event) { cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(log.BackTester, "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } var o *order.Order - o, err = bt.Portfolio.OnSignal(ev, &cs, funds) + o, err = bt.Portfolio.OnSignal(ev, &cs, bt.Funding) if err != nil { log.Errorf(log.BackTester, "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return @@ -283,10 +286,6 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Errorf(log.BackTester, "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } - if fde := ev.GetFillDependentEvent(); fde != nil { - // some events can only be triggered on a successful fill event - bt.EventQueue.AppendEvent(fde) - } err = bt.Statistic.SetEventForOffset(t) if err != nil { @@ -318,6 +317,12 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if err != nil { log.Errorf(log.BackTester, "AddComplianceSnapshotForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } + + fde := ev.GetFillDependentEvent() + if fde.IsNil() { + // some events can only be triggered on a successful fill event + bt.EventQueue.AppendEvent(fde) + } } // RunLive is a proof of concept function that does not yet support multi currency usage diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index dee2bebb384..37ead7ff179 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1222,15 +1222,13 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { ExchangeName: "ftx", Asset: asset.Futures.String(), Base: "BTC", - Quote: "0924", + Quote: "20210924", FuturesDetails: &FuturesDetails{ Leverage: Leverage{ CanUseLeverage: true, MaximumOrderLeverageRate: makerFee, }, }, - BuySide: minMax, - SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -1240,8 +1238,6 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { Base: "BTC", Quote: "USD", SpotDetails: &SpotDetails{}, - BuySide: minMax, - SellSide: minMax, MakerFee: makerFee, TakerFee: takerFee, }, @@ -1256,8 +1252,6 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { }, }, PortfolioSettings: PortfolioSettings{ - BuySide: minMax, - SellSide: minMax, Leverage: Leverage{ CanUseLeverage: true, }, diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 5d701a96ace..5b47e4a9eff 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -21,7 +21,7 @@ "exchange-name": "ftx", "asset": "futures", "base": "BTC", - "quote": "0924", + "quote": "20210924", "futures-details": { "leverage": { "can-use-leverage": true, @@ -31,14 +31,14 @@ } }, "buy-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "minimum-size": "0", + "maximum-size": "0", + "maximum-total": "0" }, "sell-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "minimum-size": "0", + "maximum-size": "0", + "maximum-total": "0" }, "min-slippage-percent": "0", "max-slippage-percent": "0", @@ -56,14 +56,14 @@ "quote": "USD", "spot-details": {}, "buy-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "minimum-size": "0", + "maximum-size": "0", + "maximum-total": "0" }, "sell-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "minimum-size": "0", + "maximum-size": "0", + "maximum-total": "0" }, "min-slippage-percent": "0", "max-slippage-percent": "0", @@ -92,14 +92,14 @@ "maximum-collateral-leverage-rate": "0" }, "buy-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "minimum-size": "0", + "maximum-size": "0", + "maximum-total": "0" }, "sell-side": { - "minimum-size": "0.005", - "maximum-size": "2", - "maximum-total": "40000" + "minimum-size": "0", + "maximum-size": "0", + "maximum-total": "0" } }, "statistic-settings": { diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 9fd3eeef4a4..ba0e557c86d 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -28,7 +28,7 @@ func (e *Exchange) Reset() { // ExecuteOrder assesses the portfolio manager's order event and if it passes validation // will send an order to the exchange/fake order manager to be stored and raise a fill event -func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager *engine.OrderManager, funds funding.IFundReleaser) (*fill.Fill, error) { +func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager *engine.OrderManager, funds funding.IFundReleaser) (fill.Event, error) { f := &fill.Fill{ Base: event.Base{ Offset: o.GetOffset(), @@ -39,9 +39,10 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * Interval: o.GetInterval(), Reason: o.GetReason(), }, - Direction: o.GetDirection(), - Amount: o.GetAmount(), - ClosePrice: data.Latest().GetClosePrice(), + Direction: o.GetDirection(), + Amount: o.GetAmount(), + ClosePrice: data.Latest().GetClosePrice(), + FillDependentEvent: o.GetFillDependentEvent(), } eventFunds := o.GetAllocatedFunds() cs, err := e.GetCurrencySettings(o.GetExchange(), o.GetAssetType(), o.Pair()) diff --git a/backtester/eventhandlers/exchange/exchange_types.go b/backtester/eventhandlers/exchange/exchange_types.go index 55ba93fe1ca..1c264af082a 100644 --- a/backtester/eventhandlers/exchange/exchange_types.go +++ b/backtester/eventhandlers/exchange/exchange_types.go @@ -27,7 +27,7 @@ var ( type ExecutionHandler interface { SetExchangeAssetCurrencySettings(asset.Item, currency.Pair, *Settings) GetCurrencySettings(string, asset.Item, currency.Pair) (Settings, error) - ExecuteOrder(order.Event, data.Handler, *engine.OrderManager, funding.IFundReleaser) (*fill.Fill, error) + ExecuteOrder(order.Event, data.Handler, *engine.OrderManager, funding.IFundReleaser) (fill.Event, error) Reset() } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b472d0be7f1..2be1b8bdea0 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -26,7 +26,7 @@ import ( // on buy/sell, the portfolio manager will size the order and assess the risk of the order // if successful, it will pass on an order.Order to be used by the exchange event handler to place an order based on // the portfolio manager's recommendations -func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds funding.IFundReserver) (*order.Order, error) { +func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds funding.IFundingReader) (*order.Order, error) { if ev == nil || cs == nil { return nil, common.ErrNilArguments } @@ -50,7 +50,8 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi Interval: ev.GetInterval(), Reason: ev.GetReason(), }, - Direction: ev.GetDirection(), + Direction: ev.GetDirection(), + FillDependentEvent: ev.GetFillDependentEvent(), } if ev.GetDirection() == "" { return o, errInvalidDirection @@ -71,22 +72,24 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi ev.GetDirection() == "" { return o, nil } + relevantFunding, err := funds.GetFundingForEvent(ev) + if err != nil { + return nil, err + } dir := ev.GetDirection() - if !funds.CanPlaceOrder(dir) { - o.AppendReason(notEnoughFundsTo + " " + dir.Lower()) - switch ev.GetDirection() { - case gctorder.Sell: - o.SetDirection(common.CouldNotSell) - case gctorder.Buy: - o.SetDirection(common.CouldNotBuy) - case gctorder.Short: - o.SetDirection(common.CouldNotShort) - case gctorder.Long: - o.SetDirection(common.CouldNotLong) + if ev.GetAssetType() == asset.Spot { + if !relevantFunding.FundReserver().CanPlaceOrder(dir) { + o.AppendReason(notEnoughFundsTo + " " + dir.Lower()) + switch ev.GetDirection() { + case gctorder.Sell: + o.SetDirection(common.CouldNotSell) + case gctorder.Buy: + o.SetDirection(common.CouldNotBuy) + } + ev.SetDirection(o.Direction) + return o, nil } - ev.SetDirection(o.Direction) - return o, nil } o.Price = ev.GetPrice() @@ -95,7 +98,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi o.SellLimit = ev.GetSellLimit() var sizingFunds decimal.Decimal if ev.GetAssetType() == asset.Spot { - pReader, err := funds.GetPairReader() + pReader, err := relevantFunding.FundReader().GetPairReader() if err != nil { return nil, err } @@ -105,26 +108,56 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi sizingFunds = pReader.QuoteAvailable() } } else if ev.GetAssetType().IsFutures() { - cReader, err := funds.GetCollateralReader() - if err != nil { - return nil, err - } - sizingFunds = cReader.AvailableFunds() - sizingFunds, err = lookup.Exchange.ScaleCollateral( - context.TODO(), "", - &gctorder.CollateralCalculator{ - CollateralCurrency: cReader.CollateralCurrency(), - Asset: ev.GetAssetType(), - Side: ev.GetDirection(), - CollateralAmount: sizingFunds, - USDPrice: ev.GetPrice(), - CalculateOffline: true, - }) - if err != nil { - return nil, err + var collatCurr currency.Code + if collatCurr = ev.GetCollateralCurrency(); collatCurr.IsEmpty() { + // get all collateral somehow? + allFunds := funds.GetAllFunding() + var calcs []gctorder.CollateralCalculator + for i := range allFunds { + calcs = append(calcs, gctorder.CollateralCalculator{ + CalculateOffline: true, + CollateralCurrency: allFunds[i].Currency, + Asset: allFunds[i].Asset, + Side: ev.GetDirection(), + CollateralAmount: allFunds[i].Available, + USDPrice: decimal.Decimal{}, + }) + } + collat, err := lookup.Exchange.CalculateTotalCollateral( + context.TODO(), + "", + true, + calcs, + ) + if err != nil { + return nil, err + } + sizingFunds = collat.TotalCollateral + } else { + allFunds := funds.GetAllFunding() + for i := range allFunds { + if allFunds[i].Exchange == ev.GetExchange() && + allFunds[i].Asset == ev.GetAssetType() && + allFunds[i].Currency.Match(collatCurr) { + sizingFunds, err = lookup.Exchange.ScaleCollateral( + context.TODO(), + "", + &gctorder.CollateralCalculator{ + CollateralCurrency: collatCurr, + Asset: ev.GetAssetType(), + Side: ev.GetDirection(), + CollateralAmount: allFunds[i].Available, + USDPrice: allFunds[i].USDPrice, + CalculateOffline: true, + }) + if err != nil { + return nil, err + } + } + } } } - sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) + sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, relevantFunding.FundReserver()) return p.evaluateOrder(ev, o, sizedOrder) } @@ -198,7 +231,7 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi } // OnFill processes the event after an order has been placed by the exchange. Its purpose is to track holdings for future portfolio decisions. -func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill.Fill, error) { +func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (fill.Event, error) { if ev == nil { return nil, common.ErrNilEvent } @@ -241,11 +274,7 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. if err != nil { log.Error(log.BackTester, err) } - fe, ok := ev.(*fill.Fill) - if !ok { - return nil, fmt.Errorf("%w expected fill event", common.ErrInvalidDataType) - } - + ev.SetExchangeFee(decimal.Zero) direction := ev.GetDirection() if direction == common.DoNothing || direction == common.CouldNotBuy || @@ -256,11 +285,10 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (*fill. direction == common.CouldNotLong || direction == common.CouldNotShort || direction == "" { - fe.ExchangeFee = decimal.Zero - return fe, nil + return ev, nil } - return fe, nil + return ev, nil } // addComplianceSnapshot gets the previous snapshot of compliance events, updates with the latest fillevent diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 538ab692885..07649b7d179 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -46,8 +46,8 @@ type Portfolio struct { // Handler contains all functions expected to operate a portfolio manager type Handler interface { - OnSignal(signal.Event, *exchange.Settings, funding.IFundReserver) (*order.Order, error) - OnFill(fill.Event, funding.IFundReleaser) (*fill.Fill, error) + OnSignal(signal.Event, *exchange.Settings, funding.IFundingReader) (*order.Order, error) + OnFill(fill.Event, funding.IFundReleaser) (fill.Event, error) GetLatestOrderSnapshotForEvent(common.EventHandler) (compliance.Snapshot, error) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) ViewHoldingAtTimePeriod(common.EventHandler) (*holdings.Holding, error) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 35357ea35c8..fe8083e058d 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -101,7 +101,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if err != nil { return nil, err } - if len(pos) == 0 { + if len(pos) == 0 || pos[len(pos)-1].Status == order.Closed { spotSignal, err := s.GetBaseData(v.spotSignal) if err != nil { return nil, err @@ -112,15 +112,22 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf } spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) spotSignal.AppendReason(fmt.Sprintf("signalling purchase of %v", spotSignal.Pair())) - futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) // first the spot purchase spotSignal.SetDirection(order.Buy) // second the futures purchase, using the newly acquired asset // as collateral to short futuresSignal.SetDirection(order.Short) - spotSignal.FillDependentEvent = &futuresSignal + futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) + futuresSignal.CollateralCurrency = spotSignal.CurrencyPair.Base spotSignal.AppendReason(fmt.Sprintf("signalling shorting %v", futuresSignal.Pair())) + // set the FillDependentEvent to use the futures signal + // as the futures signal relies on a completed spot order purchase + // to use as collateral + spotSignal.FillDependentEvent = &futuresSignal response = append(response, &spotSignal) + } else { + // analyse the conditions of the order to monitor whether + // its worthwhile to ever close the short } } return response, nil diff --git a/backtester/eventtypes/fill/fill.go b/backtester/eventtypes/fill/fill.go index 609c6225ced..989da07d67e 100644 --- a/backtester/eventtypes/fill/fill.go +++ b/backtester/eventtypes/fill/fill.go @@ -2,6 +2,7 @@ package fill import ( "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -64,3 +65,9 @@ func (f *Fill) GetOrder() *order.Detail { func (f *Fill) GetSlippageRate() decimal.Decimal { return f.Slippage } + +// GetFillDependentEvent returns the fill dependent event +// to raise after a prerequisite event has been completed +func (f *Fill) GetFillDependentEvent() signal.Event { + return f.FillDependentEvent +} diff --git a/backtester/eventtypes/fill/fill_types.go b/backtester/eventtypes/fill/fill_types.go index cf242e8d8b1..f70651c71d3 100644 --- a/backtester/eventtypes/fill/fill_types.go +++ b/backtester/eventtypes/fill/fill_types.go @@ -20,7 +20,7 @@ type Fill struct { ExchangeFee decimal.Decimal `json:"exchange-fee"` Slippage decimal.Decimal `json:"slippage"` Order *order.Detail `json:"-"` - FillDependentEvent *signal.Event + FillDependentEvent signal.Event } // Event holds all functions required to handle a fill event @@ -38,6 +38,5 @@ type Event interface { GetExchangeFee() decimal.Decimal SetExchangeFee(decimal.Decimal) GetOrder() *order.Detail - HasFillDependentEvent() bool GetFillDependentEvent() signal.Event } diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index 102ba10ad84..494a011a7f7 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -2,6 +2,7 @@ package order import ( "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -81,3 +82,9 @@ func (o *Order) SetLeverage(l decimal.Decimal) { func (o *Order) GetAllocatedFunds() decimal.Decimal { return o.AllocatedFunds } + +// GetFillDependentEvent returns the fill dependent event +// so it can be added the event queue +func (o *Order) GetFillDependentEvent() signal.Event { + return o.FillDependentEvent +} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index 133d4cb899d..ddf05e11d8a 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -21,7 +21,7 @@ type Order struct { AllocatedFunds decimal.Decimal BuyLimit decimal.Decimal SellLimit decimal.Decimal - FillDependentEvent *signal.Event + FillDependentEvent signal.Event } // Event inherits common event interfaces along with extra functions related to handling orders @@ -38,4 +38,5 @@ type Event interface { GetID() string IsLeveraged() bool GetAllocatedFunds() decimal.Decimal + GetFillDependentEvent() signal.Event } diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index 3269647766b..76aa1a36968 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -73,3 +73,19 @@ func (s *Signal) GetUnderlyingPair() (currency.Pair, error) { } return s.UnderlyingPair, nil } + +// GetFillDependentEvent returns the fill dependent event +// so it can be added to the event queue +func (s *Signal) GetFillDependentEvent() Event { + return s.FillDependentEvent +} + +// GetCollateralCurrency returns the collateral currency +func (s *Signal) GetCollateralCurrency() currency.Code { + return s.CollateralCurrency +} + +// IsNil says if the event is nil +func (s *Signal) IsNil() bool { + return s == nil +} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index a3c2fdf3982..aa0fb9d6963 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -4,6 +4,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -18,6 +19,9 @@ type Event interface { GetSellLimit() decimal.Decimal GetBuyLimit() decimal.Decimal GetAmount() decimal.Decimal + GetFillDependentEvent() Event + GetCollateralCurrency() currency.Code + IsNil() bool } // Signal contains everything needed for a strategy to raise a signal event @@ -46,4 +50,10 @@ type Signal struct { // if there is corresponding collateral in the selected currency // this enabled cash and carry strategies for example FillDependentEvent *Signal + // CollateralCurrency is an optional paramater + // when using futures to limit the collateral available + // to a singular currency + // eg with $5000 usd and 1 BTC, specifying BTC ensures + // the USD value won't be utilised when sizing an order + CollateralCurrency currency.Code } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 625e02762bf..3c31447df7b 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -442,3 +442,28 @@ func (f *FundManager) LiquidateByCollateral(c currency.Code) error { } return nil } + +// GetAllFunding returns basic representations of all current +// holdings from the latest point +func (f *FundManager) GetAllFunding() []BasicItem { + var result []BasicItem + for i := range f.items { + var usd decimal.Decimal + if f.items[i].usdTrackingCandles != nil { + latest := f.items[i].usdTrackingCandles.Latest() + if latest != nil { + usd = latest.GetClosePrice() + } + } + result = append(result, BasicItem{ + Exchange: f.items[i].exchange, + Asset: f.items[i].asset, + Currency: f.items[i].currency, + InitialFunds: f.items[i].initialFunds, + Available: f.items[i].available, + Reserved: f.items[i].reserved, + USDPrice: usd, + }) + } + return result +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 543f222fa8d..64c497b7e46 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -32,6 +32,16 @@ type IFundingManager interface { CreateSnapshot(time.Time) USDTrackingDisabled() bool LiquidateByCollateral(currency.Code) error + GetAllFunding() []BasicItem +} + +// IFundingReader is a simple interface of +// IFundingManager for readonly access at portfolio +// manager +type IFundingReader interface { + GetFundingForEvent(common.EventHandler) (IFundingPair, error) + GetFundingForEAP(string, asset.Item, currency.Pair) (IFundingPair, error) + GetAllFunding() []BasicItem } // IFundingPair allows conversion into various @@ -124,6 +134,17 @@ type Item struct { collateralCandles map[currency.Code]kline.DataFromKline } +// BasicItem is a representation of Item +type BasicItem struct { + Exchange string + Asset asset.Item + Currency currency.Code + InitialFunds decimal.Decimal + Available decimal.Decimal + Reserved decimal.Decimal + USDPrice decimal.Decimal +} + // Pair holds two currencies that are associated with each other type Pair struct { Base *Item From b3fedb33859762e2fd385084fa127261771f74f3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 25 Jan 2022 17:55:13 +1100 Subject: [PATCH 067/171] Some bug fixes --- backtester/backtest/backtest.go | 2 +- backtester/config/config_test.go | 1 - backtester/eventhandlers/exchange/exchange.go | 3 -- .../eventhandlers/portfolio/portfolio.go | 37 +++++++++++++------ .../eventhandlers/portfolio/size/size.go | 4 +- backtester/funding/funding.go | 7 ++-- 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index febe239b856..f7ef4cc91f7 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -319,7 +319,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) } fde := ev.GetFillDependentEvent() - if fde.IsNil() { + if !fde.IsNil() { // some events can only be triggered on a successful fill event bt.EventQueue.AppendEvent(fde) } diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 37ead7ff179..579ea0f7bd4 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1212,7 +1212,6 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { Asset: "spot", Currency: "USD", InitialFunds: *initialQuoteFunds2, - TransferFee: decimal.Decimal{}, }, }, }, diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index ba0e557c86d..d9a69780e97 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -51,9 +51,6 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } f.ExchangeFee = cs.ExchangeFee // defaulting to just using taker fee right now without orderbook f.Direction = o.GetDirection() - if o.GetDirection() != gctorder.Buy && o.GetDirection() != gctorder.Sell { - return f, nil - } highStr := data.StreamHigh() high := highStr[len(highStr)-1] diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 2be1b8bdea0..6b8bd234eca 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -80,15 +80,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi dir := ev.GetDirection() if ev.GetAssetType() == asset.Spot { if !relevantFunding.FundReserver().CanPlaceOrder(dir) { - o.AppendReason(notEnoughFundsTo + " " + dir.Lower()) - switch ev.GetDirection() { - case gctorder.Sell: - o.SetDirection(common.CouldNotSell) - case gctorder.Buy: - o.SetDirection(common.CouldNotBuy) - } - ev.SetDirection(o.Direction) - return o, nil + return cannotPurchase(ev, o, dir) } } @@ -137,7 +129,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi allFunds := funds.GetAllFunding() for i := range allFunds { if allFunds[i].Exchange == ev.GetExchange() && - allFunds[i].Asset == ev.GetAssetType() && + allFunds[i].Asset == asset.Spot && allFunds[i].Currency.Match(collatCurr) { sizingFunds, err = lookup.Exchange.ScaleCollateral( context.TODO(), @@ -147,21 +139,42 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi Asset: ev.GetAssetType(), Side: ev.GetDirection(), CollateralAmount: allFunds[i].Available, - USDPrice: allFunds[i].USDPrice, - CalculateOffline: true, + // GET A USD PRICE + USDPrice: allFunds[i].USDPrice, + CalculateOffline: true, }) if err != nil { return nil, err } + break } } } } + if sizingFunds.LessThanOrEqual(decimal.Zero) { + return cannotPurchase(ev, o, dir) + } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, relevantFunding.FundReserver()) return p.evaluateOrder(ev, o, sizedOrder) } +func cannotPurchase(ev signal.Event, o *order.Order, dir gctorder.Side) (*order.Order, error) { + o.AppendReason(notEnoughFundsTo + " " + dir.Lower()) + switch ev.GetDirection() { + case gctorder.Sell: + o.SetDirection(common.CouldNotSell) + case gctorder.Buy: + o.SetDirection(common.CouldNotBuy) + case gctorder.Short: + o.SetDirection(common.CouldNotShort) + case gctorder.Long: + o.SetDirection(common.CouldNotLong) + } + ev.SetDirection(o.Direction) + return o, nil +} + func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, sizedOrder *order.Order) (*order.Order, error) { var evaluatedOrder *order.Order cm, err := p.GetComplianceManager(originalOrderSignal.GetExchange(), originalOrderSignal.GetAssetType(), originalOrderSignal.Pair()) diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index d4176137ca3..002d485232b 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -25,7 +25,7 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc var amount decimal.Decimal var err error switch retOrder.GetDirection() { - case gctorder.Buy: + case gctorder.Buy, gctorder.Long: // check size against currency specific settings amount, err = s.calculateBuySize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) if err != nil { @@ -41,7 +41,7 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if amount.GreaterThan(portfolioSize) { amount = portfolioSize } - case gctorder.Sell: + case gctorder.Sell, gctorder.Short: // check size against currency specific settings amount, err = s.calculateSellSize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) if err != nil { diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 3c31447df7b..f6eb8af8a81 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -140,13 +140,14 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { if f.disableUSDTracking { return ErrUSDTrackingDisabled } + if k.Item.Asset.IsFutures() { + // futures doesn't need usd tracking? + return nil + } baseSet := false quoteSet := false var basePairedWith currency.Code for i := range f.items { - if f.items[i].asset.IsFutures() { - return nil - } if baseSet && quoteSet { return nil } From 5ee9c7b997132a125b97f7720d7eb3e358a1f234 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 25 Jan 2022 18:48:04 +1100 Subject: [PATCH 068/171] investigating funds --- .../eventhandlers/portfolio/portfolio.go | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 6b8bd234eca..cf722ad30c8 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -101,29 +101,33 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi } } else if ev.GetAssetType().IsFutures() { var collatCurr currency.Code + allFunds := funds.GetAllFunding() + var calcs []gctorder.CollateralCalculator + for i := range allFunds { + calcs = append(calcs, gctorder.CollateralCalculator{ + CalculateOffline: true, + CollateralCurrency: allFunds[i].Currency, + Asset: allFunds[i].Asset, + Side: ev.GetDirection(), + CollateralAmount: allFunds[i].Available, + USDPrice: decimal.Decimal{}, + }) + } + + // allocate this combined collateral value! + // this is preventing orders from being placed + collat, err := lookup.Exchange.CalculateTotalCollateral( + context.TODO(), + "", + true, + calcs, + ) + if err != nil { + return nil, err + } if collatCurr = ev.GetCollateralCurrency(); collatCurr.IsEmpty() { // get all collateral somehow? - allFunds := funds.GetAllFunding() - var calcs []gctorder.CollateralCalculator - for i := range allFunds { - calcs = append(calcs, gctorder.CollateralCalculator{ - CalculateOffline: true, - CollateralCurrency: allFunds[i].Currency, - Asset: allFunds[i].Asset, - Side: ev.GetDirection(), - CollateralAmount: allFunds[i].Available, - USDPrice: decimal.Decimal{}, - }) - } - collat, err := lookup.Exchange.CalculateTotalCollateral( - context.TODO(), - "", - true, - calcs, - ) - if err != nil { - return nil, err - } + sizingFunds = collat.TotalCollateral } else { allFunds := funds.GetAllFunding() From 1f286affca43a55ddf0b5ab619b1a3f80b03d1c3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 27 Jan 2022 14:49:32 +1100 Subject: [PATCH 069/171] CAN NOW CREATE A SHORT ORDER --- backtester/backtest/backtest.go | 23 ++++- backtester/backtest/setup.go | 1 + .../config/examples/ftx-cash-carry.strat | 2 +- backtester/data/data.go | 3 + backtester/eventhandlers/exchange/exchange.go | 6 ++ .../eventhandlers/portfolio/portfolio.go | 85 +++---------------- .../portfolio/portfolio_types.go | 2 +- backtester/funding/collateral.go | 4 +- backtester/funding/funding.go | 59 ++++++++++++- backtester/funding/funding_types.go | 3 + engine/rpcserver.go | 2 +- exchanges/ftx/ftx_test.go | 10 +-- exchanges/ftx/ftx_wrapper.go | 13 ++- exchanges/order/futures_types.go | 2 +- exchanges/order/orders.go | 4 +- 15 files changed, 125 insertions(+), 94 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index f7ef4cc91f7..9596be9fcac 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -121,7 +121,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { bt.Funding.CreateSnapshot(ev.GetTime()) return nil case signal.Event: - bt.processSignalEvent(eType) + bt.processSignalEvent(eType, funds.FundReserver()) case order.Event: bt.processOrderEvent(eType, funds.FundReleaser()) case fill.Event: @@ -243,14 +243,14 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } // processSignalEvent receives an event from the strategy for processing under the portfolio -func (bt *BackTest) processSignalEvent(ev signal.Event) { +func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) { cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(log.BackTester, "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } var o *order.Order - o, err = bt.Portfolio.OnSignal(ev, &cs, bt.Funding) + o, err = bt.Portfolio.OnSignal(ev, &cs, funds) if err != nil { log.Errorf(log.BackTester, "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return @@ -322,7 +322,24 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if !fde.IsNil() { // some events can only be triggered on a successful fill event bt.EventQueue.AppendEvent(fde) + // update collateral holdings + exch, err := bt.exchangeManager.GetExchangeByName(ev.GetExchange()) + if err != nil { + log.Errorf(log.BackTester, "GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + curr, err := exch.GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) + if err != nil { + log.Errorf(log.BackTester, "GetCollateralCurrencyForContract %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + + err = bt.Funding.UpdateCollateral(ev.GetExchange(), fde.GetAssetType(), curr) + if err != nil { + log.Errorf(log.BackTester, "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } } + } // RunLive is a proof of concept function that does not yet support multi currency usage diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go index b3211a29860..ea520de90b3 100644 --- a/backtester/backtest/setup.go +++ b/backtester/backtest/setup.go @@ -88,6 +88,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, } funds := funding.SetupFundingManager( + bt.exchangeManager, cfg.StrategySettings.UseExchangeLevelFunding, cfg.StrategySettings.DisableUSDTracking, ) diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 5b47e4a9eff..da3e85854e7 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -57,7 +57,7 @@ "spot-details": {}, "buy-side": { "minimum-size": "0", - "maximum-size": "0", + "maximum-size": "1", "maximum-total": "0" }, "sell-side": { diff --git a/backtester/data/data.go b/backtester/data/data.go index c0e5fa14b8b..61ce3801189 100644 --- a/backtester/data/data.go +++ b/backtester/data/data.go @@ -98,6 +98,9 @@ func (b *Base) History() []common.DataEventHandler { // Latest will return latest data event func (b *Base) Latest() common.DataEventHandler { + if b.latest == nil && len(b.stream) >= b.offset+1 { + b.latest = b.stream[b.offset] + } return b.latest } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index d9a69780e97..d0bd3265b35 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -202,6 +202,12 @@ func verifyOrderWithinLimits(f *fill.Fill, limitReducedAmount decimal.Decimal, c case gctorder.Sell: minMax = cs.SellSide direction = common.CouldNotSell + case gctorder.Long: + minMax = cs.BuySide + direction = common.CouldNotLong + case gctorder.Short: + minMax = cs.SellSide + direction = common.CouldNotShort default: direction = f.GetDirection() f.SetDirection(common.DoNothing) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index cf722ad30c8..9b5dc365cdb 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -1,7 +1,6 @@ package portfolio import ( - "context" "errors" "fmt" "time" @@ -26,7 +25,7 @@ import ( // on buy/sell, the portfolio manager will size the order and assess the risk of the order // if successful, it will pass on an order.Order to be used by the exchange event handler to place an order based on // the portfolio manager's recommendations -func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds funding.IFundingReader) (*order.Order, error) { +func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds funding.IFundReserver) (*order.Order, error) { if ev == nil || cs == nil { return nil, common.ErrNilArguments } @@ -72,16 +71,9 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi ev.GetDirection() == "" { return o, nil } - relevantFunding, err := funds.GetFundingForEvent(ev) - if err != nil { - return nil, err - } - dir := ev.GetDirection() - if ev.GetAssetType() == asset.Spot { - if !relevantFunding.FundReserver().CanPlaceOrder(dir) { - return cannotPurchase(ev, o, dir) - } + if !funds.CanPlaceOrder(dir) { + return cannotPurchase(ev, o, dir) } o.Price = ev.GetPrice() @@ -90,7 +82,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi o.SellLimit = ev.GetSellLimit() var sizingFunds decimal.Decimal if ev.GetAssetType() == asset.Spot { - pReader, err := relevantFunding.FundReader().GetPairReader() + pReader, err := funds.GetPairReader() if err != nil { return nil, err } @@ -100,65 +92,17 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi sizingFunds = pReader.QuoteAvailable() } } else if ev.GetAssetType().IsFutures() { - var collatCurr currency.Code - allFunds := funds.GetAllFunding() - var calcs []gctorder.CollateralCalculator - for i := range allFunds { - calcs = append(calcs, gctorder.CollateralCalculator{ - CalculateOffline: true, - CollateralCurrency: allFunds[i].Currency, - Asset: allFunds[i].Asset, - Side: ev.GetDirection(), - CollateralAmount: allFunds[i].Available, - USDPrice: decimal.Decimal{}, - }) - } - - // allocate this combined collateral value! - // this is preventing orders from being placed - collat, err := lookup.Exchange.CalculateTotalCollateral( - context.TODO(), - "", - true, - calcs, - ) + collateralFunds, err := funds.GetCollateralReader() if err != nil { return nil, err } - if collatCurr = ev.GetCollateralCurrency(); collatCurr.IsEmpty() { - // get all collateral somehow? - sizingFunds = collat.TotalCollateral - } else { - allFunds := funds.GetAllFunding() - for i := range allFunds { - if allFunds[i].Exchange == ev.GetExchange() && - allFunds[i].Asset == asset.Spot && - allFunds[i].Currency.Match(collatCurr) { - sizingFunds, err = lookup.Exchange.ScaleCollateral( - context.TODO(), - "", - &gctorder.CollateralCalculator{ - CollateralCurrency: collatCurr, - Asset: ev.GetAssetType(), - Side: ev.GetDirection(), - CollateralAmount: allFunds[i].Available, - // GET A USD PRICE - USDPrice: allFunds[i].USDPrice, - CalculateOffline: true, - }) - if err != nil { - return nil, err - } - break - } - } - } + sizingFunds = collateralFunds.AvailableFunds() } if sizingFunds.LessThanOrEqual(decimal.Zero) { return cannotPurchase(ev, o, dir) } - sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, relevantFunding.FundReserver()) + sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) return p.evaluateOrder(ev, o, sizedOrder) } @@ -233,10 +177,15 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi d.SetDirection(originalOrderSignal.Direction) originalOrderSignal.AppendReason("sized order to 0") } - if d.GetDirection() == gctorder.Sell { + switch d.GetDirection() { + case gctorder.Sell: + err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) + sizedOrder.AllocatedFunds = sizedOrder.Amount + case gctorder.Short, gctorder.Long: + // should we even reserve? err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) sizedOrder.AllocatedFunds = sizedOrder.Amount - } else { + default: err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.Price), gctorder.Buy) sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) } @@ -545,12 +494,6 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { return err } } - pnl, err := p.GetLatestPNLForEvent(e) - if err != nil { - return err - } - log.Infof(log.BackTester, "%+v", pnl) - return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 07649b7d179..2da1c42104c 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -46,7 +46,7 @@ type Portfolio struct { // Handler contains all functions expected to operate a portfolio manager type Handler interface { - OnSignal(signal.Event, *exchange.Settings, funding.IFundingReader) (*order.Order, error) + OnSignal(signal.Event, *exchange.Settings, funding.IFundReserver) (*order.Order, error) OnFill(fill.Event, funding.IFundReleaser) (fill.Event, error) GetLatestOrderSnapshotForEvent(common.EventHandler) (compliance.Snapshot, error) GetLatestOrderSnapshots() ([]compliance.Snapshot, error) diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 26b95b6902e..86cef1f9cee 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -61,7 +61,9 @@ func (c *Collateral) Reserve(amount decimal.Decimal, _ order.Side) error { } func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { - return c.Contract.Release(amount, decimal.Zero) + // turn this into a protected func + c.Contract.available = c.Contract.available.Add(amount) + return nil } // FundReader diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index f6eb8af8a81..079c2561d7f 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -1,6 +1,7 @@ package funding import ( + "context" "errors" "fmt" "sort" @@ -12,8 +13,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" "github.com/thrasher-corp/gocryptotrader/backtester/funding/trackingcurrencies" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/engine" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) var ( @@ -36,10 +40,11 @@ var ( // SetupFundingManager creates the funding holder. It carries knowledge about levels of funding // across all execution handlers and enables fund transfers -func SetupFundingManager(usingExchangeLevelFunding, disableUSDTracking bool) *FundManager { +func SetupFundingManager(exchManager *engine.ExchangeManager, usingExchangeLevelFunding, disableUSDTracking bool) *FundManager { return &FundManager{ usingExchangeLevelFunding: usingExchangeLevelFunding, disableUSDTracking: disableUSDTracking, + exchangeManager: exchManager, } } @@ -468,3 +473,55 @@ func (f *FundManager) GetAllFunding() []BasicItem { } return result } + +func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, collateralCurrency currency.Code) error { + exchMap := make(map[string]exchange.IBotExchange) + var collateralAmount decimal.Decimal + var err error + for i := range f.items { + if f.items[i].asset != asset.Spot { + continue + } + exch, ok := exchMap[f.items[i].exchange] + if !ok { + exch, err = f.exchangeManager.GetExchangeByName(f.items[i].exchange) + if err != nil { + return err + } + exchMap[f.items[i].exchange] = exch + } + var usd decimal.Decimal + if f.items[i].usdTrackingCandles != nil { + latest := f.items[i].usdTrackingCandles.Latest() + if latest != nil { + usd = latest.GetClosePrice() + } + } + var side = gctorder.Buy + if !f.items[i].available.GreaterThan(decimal.Zero) { + side = gctorder.Sell + } + latest, err := exch.ScaleCollateral(context.TODO(), "", &gctorder.CollateralCalculator{ + CalculateOffline: true, + CollateralCurrency: f.items[i].currency, + Asset: f.items[i].asset, + Side: side, + CollateralAmount: f.items[i].available, + CollateralPrice: usd, + }) + if err != nil { + return err + } + collateralAmount = collateralAmount.Add(latest) + } + + for i := range f.items { + if f.items[i].exchange == exchName && + f.items[i].asset == item && + f.items[i].currency.Match(collateralCurrency) { + f.items[i].available = collateralAmount + return nil + } + } + return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, exchName, item, collateralCurrency) +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 64c497b7e46..d0c61396ff1 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -7,6 +7,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -17,6 +18,7 @@ type FundManager struct { usingExchangeLevelFunding bool disableUSDTracking bool items []*Item + exchangeManager *engine.ExchangeManager } // IFundingManager limits funding usage for portfolio event handling @@ -33,6 +35,7 @@ type IFundingManager interface { USDTrackingDisabled() bool LiquidateByCollateral(currency.Code) error GetAllFunding() []BasicItem + UpdateCollateral(string, asset.Item, currency.Code) error } // IFundingReader is a simple interface of diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 894041a1d9f..34f2cae08ce 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4317,7 +4317,7 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if tick.Last == 0 { continue } - cal.USDPrice = decimal.NewFromFloat(tick.Last) + cal.CollateralPrice = decimal.NewFromFloat(tick.Last) } calculators = append(calculators, cal) } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 8909e8efc37..fafdbbc7272 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1773,7 +1773,7 @@ func TestScaleCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.NewFromFloat(tick.Price), + CollateralPrice: decimal.NewFromFloat(tick.Price), CalculateOffline: true, }) if err != nil { @@ -1793,7 +1793,7 @@ func TestScaleCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.NewFromFloat(tick.Price), + CollateralPrice: decimal.NewFromFloat(tick.Price), IsLiquidating: true, CalculateOffline: true, }) @@ -1809,7 +1809,7 @@ func TestScaleCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.Zero, + CollateralPrice: decimal.Zero, IsLiquidating: true, CalculateOffline: true, }) @@ -1858,7 +1858,7 @@ func TestCalculateTotalCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CollateralAmount: total, - USDPrice: total, + CollateralPrice: total, CalculateOffline: true, }) continue @@ -1873,7 +1873,7 @@ func TestCalculateTotalCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CollateralAmount: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.NewFromFloat(tick.Price), + CollateralPrice: decimal.NewFromFloat(tick.Price), CalculateOffline: true, }) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 6db43cc9a8b..36d911c1fa5 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1330,7 +1330,7 @@ func (f *FTX) ScaleCollateral(ctx context.Context, subAccount string, calc *orde // FTX bases scales all collateral into USD amounts return calc.CollateralAmount, nil } - if calc.USDPrice.IsZero() { + if calc.CollateralPrice.IsZero() { return decimal.Zero, fmt.Errorf("%s %s %w to scale collateral", f.Name, calc.CollateralCurrency, order.ErrUSDValueRequired) } collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] @@ -1348,9 +1348,9 @@ func (f *FTX) ScaleCollateral(ctx context.Context, subAccount string, calc *orde scaling = decimal.NewFromFloat(collateralWeight.Initial) } weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.InitialMarginFractionFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) - result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) + result = calc.CollateralAmount.Mul(calc.CollateralPrice).Mul(decimal.Min(scaling, weight)) } else { - result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) + result = result.Add(calc.CollateralAmount.Mul(calc.CollateralPrice)) } return result, nil } @@ -1479,9 +1479,6 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency } // GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair -func (f *FTX) GetCollateralCurrencyForContract(a asset.Item, _ currency.Pair) (currency.Code, error) { - if a == asset.Futures { - return currency.USD, nil - } - return currency.Code{}, errCollateralCurrencyNotFound +func (f *FTX) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) { + return currency.USD, nil } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 40badd45ec6..1f88a4e2845 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -170,7 +170,7 @@ type CollateralCalculator struct { Asset asset.Item Side Side CollateralAmount decimal.Decimal - USDPrice decimal.Decimal + CollateralPrice decimal.Decimal IsLiquidating bool } diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index 94a232e9a5b..c1dc40ffefc 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -30,7 +30,9 @@ func (s *Submit) Validate(opt ...validate.Checker) error { if s.Side != Buy && s.Side != Sell && s.Side != Bid && - s.Side != Ask { + s.Side != Ask && + s.Side != Short && + s.Side != Long { return ErrSideIsInvalid } From 3daf9f2ee25906b8d2dd4a70c8a2b758bdb3f883 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 27 Jan 2022 15:43:00 +1100 Subject: [PATCH 070/171] Minor change in short size --- backtester/config/examples/ftx-cash-carry.strat | 2 +- backtester/eventhandlers/exchange/exchange.go | 3 +++ backtester/eventhandlers/portfolio/portfolio.go | 15 ++++++++++++--- .../strategies/ftxcashandcarry/ftxcashandcarry.go | 1 - .../ftxcashandcarry/ftxcashandcarry_types.go | 8 +++++--- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index da3e85854e7..5b47e4a9eff 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -57,7 +57,7 @@ "spot-details": {}, "buy-side": { "minimum-size": "0", - "maximum-size": "1", + "maximum-size": "0", "maximum-total": "0" }, "sell-side": { diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index d0bd3265b35..15cab8304c5 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -44,6 +44,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ClosePrice: data.Latest().GetClosePrice(), FillDependentEvent: o.GetFillDependentEvent(), } + if o.GetAssetType().IsFutures() { + f.Amount = o.GetAllocatedFunds() + } eventFunds := o.GetAllocatedFunds() cs, err := e.GetCurrencySettings(o.GetExchange(), o.GetAssetType(), o.Pair()) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 9b5dc365cdb..2e40ea80838 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -158,6 +158,10 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi originalOrderSignal.Direction = common.CouldNotBuy case gctorder.Sell: originalOrderSignal.Direction = common.CouldNotSell + case gctorder.Long: + originalOrderSignal.Direction = common.CouldNotLong + case gctorder.Short: + originalOrderSignal.Direction = common.CouldNotShort default: originalOrderSignal.Direction = common.DoNothing } @@ -171,6 +175,10 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi originalOrderSignal.Direction = common.CouldNotBuy case gctorder.Sell: originalOrderSignal.Direction = common.CouldNotSell + case gctorder.Long: + originalOrderSignal.Direction = common.CouldNotLong + case gctorder.Short: + originalOrderSignal.Direction = common.CouldNotShort default: originalOrderSignal.Direction = common.DoNothing } @@ -182,9 +190,8 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) sizedOrder.AllocatedFunds = sizedOrder.Amount case gctorder.Short, gctorder.Long: - // should we even reserve? - err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) - sizedOrder.AllocatedFunds = sizedOrder.Amount + err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + sizedOrder.AllocatedFunds = sizedOrder.Amount.Div(sizedOrder.Price) default: err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.Price), gctorder.Buy) sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) @@ -494,6 +501,8 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { return err } } + pos := settings.FuturesTracker.GetPositions() + log.Debugf(log.BackTester, "%+v", pos) return nil } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index fe8083e058d..1967c1acfbe 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -57,7 +57,6 @@ func sortSignals(d []data.Handler, f funding.IFundTransferer) (map[currency.Pair a := l.GetAssetType() switch { case a == asset.Spot: - entry := response[l.Pair().Format("", false)] entry.spotSignal = d[i] response[l.Pair().Format("", false)] = entry diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go index 306def1c6ec..c2da213ea51 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go @@ -9,9 +9,11 @@ import ( const ( // Name is the strategy name - Name = "ftx-cash-carry" - description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` - exchangeName = "ftx" + Name = "ftx-cash-carry" + description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` + exchangeName = "ftx" + spotDifferenceEntry = "spot-difference-entry" + spotDifferenceExit = "spot-difference-exit" ) var ( From 406f26c66e113d726534685a657726dad8f7fd04 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 28 Jan 2022 15:31:19 +1100 Subject: [PATCH 071/171] Fixes for unrealised PNL & collateral rendering --- cmd/gctcli/commands.go | 2 +- engine/order_manager.go | 21 + engine/order_manager_test.go | 47 + engine/rpcserver.go | 33 +- engine/rpcserver_test.go | 8 +- exchanges/ftx/ftx.go | 4 +- exchanges/ftx/ftx_test.go | 23 +- exchanges/ftx/ftx_wrapper.go | 23 +- exchanges/order/futures.go | 73 +- exchanges/order/futures_test.go | 58 ++ exchanges/order/futures_types.go | 3 +- gctrpc/rpc.pb.go | 1433 +++++++++++++++--------------- gctrpc/rpc.proto | 7 +- gctrpc/rpc.swagger.json | 3 + 14 files changed, 995 insertions(+), 743 deletions(-) diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 28238d733c6..47b4fd7f073 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4755,7 +4755,7 @@ var getFuturesPositionsCommand = &cli.Command{ Name: "end", Aliases: []string{"ed"}, Usage: " rounded down to the nearest hour, ensure your last position is within this window for accurate calculations", - Value: time.Now().Truncate(time.Hour).Format(common.SimpleTimeFormat), + Value: time.Now().Format(common.SimpleTimeFormat), Destination: &endTime, }, &cli.IntFlag{ diff --git a/engine/order_manager.go b/engine/order_manager.go index a89dae19513..e2f11fc71ea 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gofrs/uuid" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/communications/base" "github.com/thrasher-corp/gocryptotrader/currency" @@ -263,6 +264,26 @@ func (m *OrderManager) ClearFuturesTracking(exch string, item asset.Item, pair c return m.orderStore.futuresPositionController.ClearPositionsForExchange(exch, item, pair) } +// UpdateOpenPositionUnrealisedPNL finds an open position from +// an exchange asset pair, then calculates the unrealisedPNL +// using the latest ticker data +func (m *OrderManager) UpdateOpenPositionUnrealisedPNL(e string, item asset.Item, pair currency.Pair, last float64, updated time.Time) (decimal.Decimal, error) { + if m == nil { + return decimal.Zero, fmt.Errorf("order manager %w", ErrNilSubsystem) + } + if atomic.LoadInt32(&m.started) == 0 { + return decimal.Zero, fmt.Errorf("order manager %w", ErrSubSystemNotStarted) + } + if m.orderStore.futuresPositionController == nil { + return decimal.Zero, errFuturesTrackerNotSetup + } + if !item.IsFutures() { + return decimal.Zero, fmt.Errorf("%v %w", item, order.ErrNotFuturesAsset) + } + + return m.orderStore.futuresPositionController.UpdateOpenPositionUnrealisedPNL(e, item, pair, last, updated) +} + // GetOrderInfo calls the exchange's wrapper GetOrderInfo function // and stores the result in the order manager func (m *OrderManager) GetOrderInfo(ctx context.Context, exchangeName, orderID string, cp currency.Pair, a asset.Item) (order.Detail, error) { diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 7353755086b..895ec835abd 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" @@ -1259,6 +1260,52 @@ func TestClearFuturesPositionsForExchange(t *testing.T) { t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) } } + +func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { + t.Parallel() + o := &OrderManager{} + cp := currency.NewPair(currency.BTC, currency.USDT) + _, err := o.UpdateOpenPositionUnrealisedPNL("test", asset.Spot, cp, 1, time.Now()) + if !errors.Is(err, ErrSubSystemNotStarted) { + t.Errorf("received '%v', expected '%v'", err, ErrSubSystemNotStarted) + } + o.started = 1 + _, err = o.UpdateOpenPositionUnrealisedPNL("test", asset.Spot, cp, 1, time.Now()) + if !errors.Is(err, errFuturesTrackerNotSetup) { + t.Errorf("received '%v', expected '%v'", err, errFuturesTrackerNotSetup) + } + o.orderStore.futuresPositionController = order.SetupPositionController() + _, err = o.UpdateOpenPositionUnrealisedPNL("test", asset.Spot, cp, 1, time.Now()) + if !errors.Is(err, order.ErrNotFuturesAsset) { + t.Errorf("received '%v', expected '%v'", err, order.ErrNotFuturesAsset) + } + + _, err = o.UpdateOpenPositionUnrealisedPNL("test", asset.Futures, cp, 1, time.Now()) + if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) + } + + err = o.orderStore.futuresPositionController.TrackNewOrder(&order.Detail{ + ID: "test", + Date: time.Now(), + Exchange: "test", + AssetType: asset.Futures, + Pair: cp, + Side: order.Buy, + Amount: 1, + Price: 1}) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + unrealised, err := o.UpdateOpenPositionUnrealisedPNL("test", asset.Futures, cp, 2, time.Now()) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + if !unrealised.Equal(decimal.NewFromInt(1)) { + t.Errorf("received '%v', expected '%v'", unrealised, 1) + } +} + func TestSubmitFakeOrder(t *testing.T) { t.Parallel() o := &OrderManager{} diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 9a3ea056dbb..2aca6a0f8d0 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -4181,6 +4181,17 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if r.PositionLimit > 0 && len(response.Positions) >= int(r.PositionLimit) { break } + if pos[i].Status == order.Open { + var tick *ticker.Price + tick, err = exch.FetchTicker(ctx, pos[i].Pair, pos[i].Asset) + if err != nil { + return nil, fmt.Errorf("%w when fetching ticker data for %v %v %v", err, pos[i].Exchange, pos[i].Asset, pos[i].Pair) + } + pos[i].UnrealisedPNL, err = s.OrderManager.UpdateOpenPositionUnrealisedPNL(pos[i].Exchange, pos[i].Asset, pos[i].Pair, tick.Last, tick.LastUpdated) + if err != nil { + return nil, fmt.Errorf("%w when updating unrealised PNL for %v %v %v", err, pos[i].Exchange, pos[i].Asset, pos[i].Pair) + } + } response.TotalOrders += int64(len(pos[i].Orders)) details := &gctrpc.FuturePosition{ Status: pos[i].Status.String(), @@ -4223,7 +4234,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture Total: pos[i].Orders[j].Trades[k].Total, }) } - details.Orders = append(details.Orders, &gctrpc.OrderDetails{ + od := &gctrpc.OrderDetails{ Exchange: pos[i].Orders[j].Exchange, Id: pos[i].Orders[j].ID, ClientOrderId: pos[i].Orders[j].ClientOrderID, @@ -4233,14 +4244,17 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture OrderSide: pos[i].Orders[j].Side.String(), OrderType: pos[i].Orders[j].Type.String(), CreationTime: pos[i].Orders[j].Date.Unix(), - UpdateTime: pos[i].Orders[j].LastUpdated.Unix(), Status: pos[i].Orders[j].Status.String(), Price: pos[i].Orders[j].Price, Amount: pos[i].Orders[j].Amount, Fee: pos[i].Orders[j].Fee, Cost: pos[i].Orders[j].Cost, Trades: trades, - }) + } + if pos[i].Orders[j].LastUpdated.After(pos[i].Orders[j].Date) { + od.UpdateTime = pos[i].Orders[j].LastUpdated.Unix() + } + details.Orders = append(details.Orders, od) } response.Positions = append(response.Positions, details) } @@ -4333,14 +4347,23 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe } if r.IncludeBreakdown { for i := range collateral.BreakdownByCurrency { - if collateral.BreakdownByCurrency[i].Amount.IsZero() && !r.IncludeZeroValues { + if collateral.BreakdownByCurrency[i].ScaledValue.IsZero() && !r.IncludeZeroValues { continue } cb := &gctrpc.CollateralForCurrency{ Currency: collateral.BreakdownByCurrency[i].Currency.String(), - ScaledCollateral: collateral.BreakdownByCurrency[i].Amount.String(), ScaledToCurrency: collateral.BreakdownByCurrency[i].ValueCurrency.String(), } + if collateral.BreakdownByCurrency[i].ScaledValue.GreaterThan(decimal.Zero) { + cb.ScaledCollateral = collateral.BreakdownByCurrency[i].ScaledValue.String() + } + if collateral.BreakdownByCurrency[i].OriginalValue.GreaterThan(decimal.Zero) { + cb.OriginalAmount = collateral.BreakdownByCurrency[i].OriginalValue.String() + } + if collateral.BreakdownByCurrency[i].OriginalValue.IsZero() && + collateral.BreakdownByCurrency[i].ScaledValue.IsZero() { + continue + } if collateral.BreakdownByCurrency[i].Error != nil { cb.Error = collateral.BreakdownByCurrency[i].Error.Error() } diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index dc9b6fe4b57..e944796b854 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -137,17 +137,17 @@ func (f fExchange) CalculateTotalCollateral(context.Context, string, bool, []ord TotalCollateral: decimal.NewFromInt(1337), BreakdownByCurrency: []order.CollateralByCurrency{ { - Currency: currency.USD, - Amount: decimal.NewFromInt(1330), + Currency: currency.USD, + ScaledValue: decimal.NewFromInt(1330), }, { Currency: currency.DOGE, - Amount: decimal.NewFromInt(10), + ScaledValue: decimal.NewFromInt(10), ValueCurrency: currency.USD, }, { Currency: currency.XRP, - Amount: decimal.NewFromInt(-3), + ScaledValue: decimal.NewFromInt(-3), ValueCurrency: currency.USD, }, }, diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 4cd24c8fdd4..6573d763273 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1681,10 +1681,10 @@ func (c CollateralWeightHolder) loadInitialMarginFraction(code string, imf float c[code] = currencyCollateral } -func (c CollateralWeightHolder) load(code string, initial, total, imfFactor float64) { +func (c CollateralWeightHolder) load(code string, total, initial, imfFactor float64) { c[code] = CollateralWeight{ - Initial: initial, Total: total, + Initial: initial, InitialMarginFractionFactor: imfFactor, } } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 9486d650e6b..e45ce99853d 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1736,8 +1736,28 @@ func TestUpdateOrderExecutionLimits(t *testing.T) { func TestScaleCollateral(t *testing.T) { t.Parallel() + + result, err := f.ScaleCollateral( + context.Background(), + "", + &order.CollateralCalculator{ + CollateralCurrency: currency.USDT, + Asset: asset.Spot, + Side: order.Buy, + CalculateOffline: true, + CollateralAmount: decimal.NewFromInt(100000), + USDPrice: decimal.NewFromFloat(1.0003), + }) + if err != nil { + t.Error(err) + } + expectedUSDValue := decimal.NewFromFloat(95028.5) + if !result.Equal(expectedUSDValue) { + t.Errorf("received %v expected %v", result, expectedUSDValue) + } + if !areTestAPIKeysSet() { - t.Skip("skipping test, api keys not set") + return } accountInfo, err := f.GetAccountInfo(context.Background(), subaccount) if err != nil { @@ -1836,6 +1856,7 @@ func TestScaleCollateral(t *testing.T) { if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } + } func TestCalculateTotalCollateral(t *testing.T) { diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index d9d024e6d76..5efdde6123d 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1347,7 +1347,11 @@ func (f *FTX) ScaleCollateral(ctx context.Context, subAccount string, calc *orde } else { scaling = decimal.NewFromFloat(collateralWeight.Initial) } - weight := decimal.NewFromFloat(1.1 / (1 + collateralWeight.InitialMarginFractionFactor*math.Sqrt(calc.CollateralAmount.InexactFloat64()))) + one := decimal.NewFromInt(1) + sqrt := decimal.NewFromFloat(math.Sqrt(calc.CollateralAmount.InexactFloat64())) + onePointOne := decimal.NewFromFloat(1.1) + imf := decimal.NewFromFloat(collateralWeight.InitialMarginFractionFactor) + weight := onePointOne.Div(one.Add(imf.Mul(sqrt))) result = calc.CollateralAmount.Mul(calc.USDPrice).Mul(decimal.Min(scaling, weight)) } else { result = result.Add(calc.CollateralAmount.Mul(calc.USDPrice)) @@ -1403,11 +1407,16 @@ func (f *FTX) CalculateTotalCollateral(ctx context.Context, subAccount string, c scaled := wallet[y].CollateralWeight * balances[z].USDValue dScaled := decimal.NewFromFloat(scaled) result.TotalCollateral = result.TotalCollateral.Add(dScaled) - result.BreakdownByCurrency = append(result.BreakdownByCurrency, order.CollateralByCurrency{ - Currency: collateralAssets[x].CollateralCurrency, - Amount: dScaled, - ValueCurrency: currency.USD, - }) + breakDown := order.CollateralByCurrency{ + Currency: collateralAssets[x].CollateralCurrency, + + OriginalValue: collateralAssets[x].CollateralAmount, + } + if !collateralAssets[x].CollateralCurrency.Match(currency.USD) { + breakDown.ScaledValue = dScaled + breakDown.ValueCurrency = currency.USD + } + result.BreakdownByCurrency = append(result.BreakdownByCurrency, breakDown) break wallets } } @@ -1432,7 +1441,7 @@ func (f *FTX) CalculateTotalCollateral(ctx context.Context, subAccount string, c return nil, err } result.TotalCollateral = result.TotalCollateral.Add(collateral) - curr.Amount = collateral + curr.ScaledValue = collateral if !collateralAssets[i].CollateralCurrency.Match(currency.USD) { curr.ValueCurrency = currency.USD } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 4936325a22d..c98917b7196 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -91,6 +91,50 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite return multiPositionTracker.GetPositions(), nil } +// UpdateOpenPositionUnrealisedPNL finds an open position from +// an exchange asset pair, then calculates the unrealisedPNL +// using the latest ticker data +func (c *PositionController) UpdateOpenPositionUnrealisedPNL(exch string, item asset.Item, pair currency.Pair, last float64, updated time.Time) (decimal.Decimal, error) { + if c == nil { + return decimal.Zero, common.ErrNilPointer + } + c.m.Lock() + defer c.m.Unlock() + if !item.IsFutures() { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) + } + exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + if !ok { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) + } + itemM, ok := exchM[item] + if !ok { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForAsset) + } + multiPositionTracker, ok := itemM[pair] + if !ok { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) + } + + multiPositionTracker.m.Lock() + defer multiPositionTracker.m.Unlock() + pos := multiPositionTracker.positions + if len(pos) == 0 { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) + } + latestPos := pos[len(pos)-1] + if latestPos.status != Open { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionClosed) + } + err := latestPos.TrackPNLByTime(updated, last) + if err != nil { + return decimal.Zero, fmt.Errorf("%w for position %v %v %v", err, exch, item, pair) + } + latestPos.m.Lock() + defer latestPos.m.Unlock() + return latestPos.unrealisedPNL, nil +} + // ClearPositionsForExchange resets positions for an // exchange, asset, pair that has been stored func (c *PositionController) ClearPositionsForExchange(exch string, item asset.Item, pair currency.Pair) error { @@ -281,13 +325,16 @@ func (p *PositionTracker) GetStats() PositionStats { } p.m.Lock() defer p.m.Unlock() + var orders []Detail + orders = append(orders, p.longPositions...) + orders = append(orders, p.shortPositions...) return PositionStats{ Exchange: p.exchange, Asset: p.asset, Pair: p.contractPair, Underlying: p.underlyingAsset, Status: p.status, - Orders: append(p.longPositions, p.shortPositions...), + Orders: orders, RealisedPNL: p.realisedPNL, UnrealisedPNL: p.unrealisedPNL, LatestDirection: p.currentDirection, @@ -314,15 +361,20 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro Time: t, Price: price, } - diff := price.Sub(p.entryPrice) - result.UnrealisedPNL = p.exposure.Mul(diff) - result.Price = price + if p.currentDirection.IsLong() { + diff := price.Sub(p.entryPrice) + result.UnrealisedPNL = p.exposure.Mul(diff) + } else if p.currentDirection.IsShort() { + diff := p.entryPrice.Sub(price) + result.UnrealisedPNL = p.exposure.Mul(diff) + } if len(p.pnlHistory) > 0 { result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees result.Exposure = p.pnlHistory[len(p.pnlHistory)-1].Exposure } var err error p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) + p.unrealisedPNL = result.UnrealisedPNL return err } @@ -382,11 +434,13 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.entryPrice = decimal.NewFromFloat(d.Price) } + var updated bool for i := range p.shortPositions { if p.shortPositions[i].ID == d.ID { ord := p.shortPositions[i].Copy() ord.UpdateOrderFromDetail(d) p.shortPositions[i] = ord + updated = true break } } @@ -395,14 +449,17 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { ord := p.longPositions[i].Copy() ord.UpdateOrderFromDetail(d) p.longPositions[i] = ord + updated = true break } } - if d.Side.IsShort() { - p.shortPositions = append(p.shortPositions, d.Copy()) - } else { - p.longPositions = append(p.longPositions, d.Copy()) + if !updated { + if d.Side.IsShort() { + p.shortPositions = append(p.shortPositions, d.Copy()) + } else { + p.longPositions = append(p.longPositions, d.Copy()) + } } var shortSide, longSide decimal.Decimal diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index d79d188773a..9ffd3c3f492 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -729,3 +729,61 @@ func TestTrackPNLByTime(t *testing.T) { t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) } } + +func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { + t.Parallel() + pc := SetupPositionController() + + pnl, err := pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + if !errors.Is(err, ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + + pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Spot, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + if !errors.Is(err, ErrNotFuturesAsset) { + t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) + } + + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + Side: Long, + ID: "lol", + Price: 1, + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + + pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi2", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + if !errors.Is(err, ErrPositionsNotLoadedForExchange) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) + } + + pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.PerpetualSwap, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + if !errors.Is(err, ErrPositionsNotLoadedForAsset) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForAsset) + } + + pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.DOGE), 2, time.Now()) + if !errors.Is(err, ErrPositionsNotLoadedForPair) { + t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) + } + + pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v", err, nil) + } + if !pnl.Equal(decimal.NewFromInt(1)) { + t.Errorf("received '%v' expected '%v", pnl, 1) + } + + pc = nil + pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) + } +} diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 9c6897698b8..39eef9c332b 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -70,7 +70,8 @@ type TotalCollateralResponse struct { // eg in FTX ValueCurrency is USD type CollateralByCurrency struct { Currency currency.Code - Amount decimal.Decimal + OriginalValue decimal.Decimal + ScaledValue decimal.Decimal ValueCurrency currency.Code Error error } diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 477bcdb407c..826cfd39658 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -11214,9 +11214,10 @@ type CollateralForCurrency struct { unknownFields protoimpl.UnknownFields Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` - ScaledCollateral string `protobuf:"bytes,2,opt,name=scaledCollateral,proto3" json:"scaledCollateral,omitempty"` - ScaledToCurrency string `protobuf:"bytes,3,opt,name=scaledToCurrency,proto3" json:"scaledToCurrency,omitempty"` - Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` + OriginalAmount string `protobuf:"bytes,2,opt,name=originalAmount,proto3" json:"originalAmount,omitempty"` + ScaledCollateral string `protobuf:"bytes,3,opt,name=scaledCollateral,proto3" json:"scaledCollateral,omitempty"` + ScaledToCurrency string `protobuf:"bytes,4,opt,name=scaledToCurrency,proto3" json:"scaledToCurrency,omitempty"` + Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"` } func (x *CollateralForCurrency) Reset() { @@ -11258,6 +11259,13 @@ func (x *CollateralForCurrency) GetCurrency() string { return "" } +func (x *CollateralForCurrency) GetOriginalAmount() string { + if x != nil { + return x.OriginalAmount + } + return "" +} + func (x *CollateralForCurrency) GetScaledCollateral() string { if x != nil { return x.ScaledCollateral @@ -12898,733 +12906,736 @@ var file_rpc_proto_rawDesc = []byte{ 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xa1, 0x01, 0x0a, 0x15, 0x43, 0x6f, + 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xc9, 0x01, 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, - 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x10, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x83, 0x59, - 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, - 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, - 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, - 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, - 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, - 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, - 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, - 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, - 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, - 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, - 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, - 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x26, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, + 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, + 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, - 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, - 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, - 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, - 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, - 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, - 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, - 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, - 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, - 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, - 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, - 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, - 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, - 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, - 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, - 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, - 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, - 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, - 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, - 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, - 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, - 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, - 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, - 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, + 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, + 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, + 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, + 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, + 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, + 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, + 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, - 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, - 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, + 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, + 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, + 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, + 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, + 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, + 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, + 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, + 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, + 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, + 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, + 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, + 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, + 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, + 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, + 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, + 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, + 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, + 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, + 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, - 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, - 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, - 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, - 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, - 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, + 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, + 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, + 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, + 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, + 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, + 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, + 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, + 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, + 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, + 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, + 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, + 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, + 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, + 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, + 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, + 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, + 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, + 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, - 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, - 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, - 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, - 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, - 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, + 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, + 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, + 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, + 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, + 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, + 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, + 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, + 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, + 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, + 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, + 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, + 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, + 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, + 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, + 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, + 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, + 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, + 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, - 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, - 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, - 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, - 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, - 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, - 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, - 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, + 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, + 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, + 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, + 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, + 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, + 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, + 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, + 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, + 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, + 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, - 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, - 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, - 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, - 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, - 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, - 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, - 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, + 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, + 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, + 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, - 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, - 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, - 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, - 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, - 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, - 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, + 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, + 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, + 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, + 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, + 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, + 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, + 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, + 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, + 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, + 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, + 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, + 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, + 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, + 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, - 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, - 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, - 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, - 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, - 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, - 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, - 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, - 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, + 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, + 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index e09eb669d48..1f5290a373e 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1071,9 +1071,10 @@ message GetCollateralResponse { message CollateralForCurrency { string currency = 1; - string scaledCollateral = 2; - string scaledToCurrency = 3; - string error = 4; + string originalAmount = 2; + string scaledCollateral = 3; + string scaledToCurrency = 4; + string error = 5; } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index a01edb9ad76..cb444b6ab17 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -3634,6 +3634,9 @@ "currency": { "type": "string" }, + "originalAmount": { + "type": "string" + }, "scaledCollateral": { "type": "string" }, From b4c0ae86ebbd7c1680d72c132d5f462cdae34566 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 28 Jan 2022 16:02:21 +1100 Subject: [PATCH 072/171] Fixes lint and tests --- engine/rpcserver_test.go | 26 ++++++++++++---------- exchanges/ftx/ftx_test.go | 11 +++++----- exchanges/order/futures.go | 26 ++++++++++++---------- exchanges/order/futures_test.go | 39 +++++++++++++++++++++++---------- 4 files changed, 61 insertions(+), 41 deletions(-) diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index e944796b854..d9f0f1b6791 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -2029,31 +2029,34 @@ func TestCurrencyStateTradingPair(t *testing.T) { func TestGetFuturesPositions(t *testing.T) { t.Parallel() em := SetupExchangeManager() - exch, err := em.NewExchangeByName(testExchange) + exch, err := em.NewExchangeByName("ftx") if err != nil { t.Fatal(err) } + exch.SetDefaults() b := exch.GetBase() b.Name = fakeExchangeName b.Enabled = true - cp, err := currency.NewPairFromString("btc-usd") + cp, err := currency.NewPairFromString("btc-perp") if err != nil { t.Fatal(err) } b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ - AssetEnabled: convert.BoolPtr(true), - ConfigFormat: ¤cy.PairFormat{}, - Available: currency.Pairs{cp}, - Enabled: currency.Pairs{cp}, + AssetEnabled: convert.BoolPtr(true), + RequestFormat: ¤cy.PairFormat{Delimiter: "-"}, + ConfigFormat: ¤cy.PairFormat{Delimiter: "-"}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, } b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{ - AssetEnabled: convert.BoolPtr(true), - ConfigFormat: ¤cy.PairFormat{}, - Available: currency.Pairs{cp}, - Enabled: currency.Pairs{cp}, + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{Delimiter: "/"}, + RequestFormat: ¤cy.PairFormat{Delimiter: "/"}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, } fakeExchange := fExchange{ IBotExchange: exch, @@ -2069,7 +2072,8 @@ func TestGetFuturesPositions(t *testing.T) { Engine: &Engine{ ExchangeManager: em, currencyStateManager: &CurrencyStateManager{ - started: 1, iExchangeManager: em, + started: 1, + iExchangeManager: em, }, OrderManager: om, }, diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index e45ce99853d..94665908d5e 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1856,7 +1856,6 @@ func TestScaleCollateral(t *testing.T) { if (math.Abs((localScaling-accountInfo.Collateral)/accountInfo.Collateral) * 100) > 5 { t.Errorf("collateral scaling less than 95%% accurate, received '%v' expected roughly '%v'", localScaling, accountInfo.Collateral) } - } func TestCalculateTotalCollateral(t *testing.T) { @@ -2040,11 +2039,11 @@ func TestLoadCollateralWeight(t *testing.T) { if !ok { t.Fatal("expected loaded collateral weight") } - if cw.Initial != 1 { - t.Errorf("expected '1', received '%v'", cw.Total) + if cw.Total != 1 { + t.Errorf("expected '1', received '%v'", cw.InitialMarginFractionFactor) } - if cw.Total != 2 { - t.Errorf("expected '2', received '%v'", cw.InitialMarginFractionFactor) + if cw.Initial != 2 { + t.Errorf("expected '2', received '%v'", cw.Total) } if cw.InitialMarginFractionFactor != 3 { t.Errorf("expected '3', received '%v'", cw.Total) @@ -2073,7 +2072,7 @@ func TestGetExpiredFutures(t *testing.T) { func TestGetExpiredFuture(t *testing.T) { t.Parallel() - _, err := f.GetExpiredFuture(context.Background(), currency.NewPairWithDelimiter("BTC", "1231", "-")) + _, err := f.GetExpiredFuture(context.Background(), currency.NewPairWithDelimiter("BTC", "20211231", "-")) if err != nil { t.Error(err) } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index c98917b7196..c07172794b5 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -436,22 +436,24 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { var updated bool for i := range p.shortPositions { - if p.shortPositions[i].ID == d.ID { - ord := p.shortPositions[i].Copy() - ord.UpdateOrderFromDetail(d) - p.shortPositions[i] = ord - updated = true - break + if p.shortPositions[i].ID != d.ID { + continue } + ord := p.shortPositions[i].Copy() + ord.UpdateOrderFromDetail(d) + p.shortPositions[i] = ord + updated = true + break } for i := range p.longPositions { - if p.longPositions[i].ID == d.ID { - ord := p.longPositions[i].Copy() - ord.UpdateOrderFromDetail(d) - p.longPositions[i] = ord - updated = true - break + if p.longPositions[i].ID != d.ID { + continue } + ord := p.longPositions[i].Copy() + ord.UpdateOrderFromDetail(d) + p.longPositions[i] = ord + updated = true + break } if !updated { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 9ffd3c3f492..5bcf6c2a252 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -280,7 +280,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Short, - ID: "1", + ID: "2", Amount: 1, }) if !errors.Is(err, nil) { @@ -296,7 +296,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Long, - ID: "2", + ID: "3", Amount: 2, }) if !errors.Is(err, nil) { @@ -316,11 +316,26 @@ func TestExchangeTrackNewOrder(t *testing.T) { AssetType: item, Pair: pair, Side: Long, - ID: "2", + ID: "4", + Amount: 2, + }) + if !errors.Is(err, errPositionDiscrepancy) { + t.Errorf("received '%v' expected '%v", err, errPositionDiscrepancy) + } + + resp.positions = []*PositionTracker{resp.positions[0]} + resp.positions[0].status = Closed + err = resp.TrackNewOrder(&Detail{ + Date: tt, + Exchange: exch, + AssetType: item, + Pair: pair, + Side: Long, + ID: "4", Amount: 2, }) if !errors.Is(err, nil) { - t.Error(err) + t.Errorf("received '%v' expected '%v", err, nil) } if len(resp.positions) != 2 { t.Errorf("expected '2' received %v", len(resp.positions)) @@ -333,7 +348,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { Pair: pair, AssetType: asset.USDTMarginedFutures, Side: Long, - ID: "2", + ID: "5", Amount: 2, }) if !errors.Is(err, errAssetMismatch) { @@ -734,12 +749,12 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { t.Parallel() pc := SetupPositionController() - pnl, err := pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + _, err := pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) if !errors.Is(err, ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Spot, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + _, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Spot, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) if !errors.Is(err, ErrNotFuturesAsset) { t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) } @@ -758,22 +773,22 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { t.Errorf("received '%v' expected '%v", err, nil) } - pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi2", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + _, err = pc.UpdateOpenPositionUnrealisedPNL("hi2", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) if !errors.Is(err, ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.PerpetualSwap, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + _, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.PerpetualSwap, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) if !errors.Is(err, ErrPositionsNotLoadedForAsset) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForAsset) } - pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.DOGE), 2, time.Now()) + _, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.DOGE), 2, time.Now()) if !errors.Is(err, ErrPositionsNotLoadedForPair) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) } - pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + pnl, err := pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v", err, nil) } @@ -782,7 +797,7 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { } pc = nil - pnl, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) + _, err = pc.UpdateOpenPositionUnrealisedPNL("hi", asset.Futures, currency.NewPair(currency.BTC, currency.USDT), 2, time.Now()) if !errors.Is(err, common.ErrNilPointer) { t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) } From 932a6177384e2ae8ff9f6b24eaf1b6a25c343c2c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 31 Jan 2022 11:28:32 +1100 Subject: [PATCH 073/171] Adds some verbosity --- backtester/backtest/setup.go | 3 ++- backtester/eventhandlers/portfolio/portfolio.go | 2 -- backtester/main.go | 9 +++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backtester/backtest/setup.go b/backtester/backtest/setup.go index ea520de90b3..2aef5b0bccc 100644 --- a/backtester/backtest/setup.go +++ b/backtester/backtest/setup.go @@ -42,7 +42,7 @@ import ( ) // NewFromConfig takes a strategy config and configures a backtester variable to run -func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, error) { +func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool) (*BackTest, error) { log.Infoln(log.BackTester, "loading config...") if cfg == nil { return nil, errNilConfig @@ -138,6 +138,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string) (*BackTest, conf.Websocket = convert.BoolPtr(false) conf.WebsocketResponseCheckTimeout = time.Second conf.WebsocketResponseMaxLimit = time.Second + conf.Verbose = verbose err = exch.Setup(conf) if err != nil { return nil, err diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 2e40ea80838..4743ef2673b 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -501,8 +501,6 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { return err } } - pos := settings.FuturesTracker.GetPositions() - log.Debugf(log.BackTester, "%+v", pos) return nil } diff --git a/backtester/main.go b/backtester/main.go index 9dda3a0c571..458e4780f32 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -15,7 +15,7 @@ import ( func main() { var configPath, templatePath, reportOutput string - var printLogo, generateReport, darkReport bool + var printLogo, generateReport, darkReport, verbose bool wd, err := os.Getwd() if err != nil { fmt.Printf("Could not get working directory. Error: %v.\n", err) @@ -60,6 +60,11 @@ func main() { "darkreport", false, "sets the output report to use a dark theme by default") + flag.BoolVar( + &verbose, + "verbose", + false, + "if enabled, will set exchange requests to verbose for debugging purposes") flag.Parse() var bt *backtest.BackTest @@ -85,7 +90,7 @@ func main() { fmt.Printf("Could not read config. Error: %v.\n", err) os.Exit(1) } - bt, err = backtest.NewFromConfig(cfg, templatePath, reportOutput) + bt, err = backtest.NewFromConfig(cfg, templatePath, reportOutput, verbose) if err != nil { fmt.Printf("Could not setup backtester from config. Error: %v.\n", err) os.Exit(1) From 596a0b8adcc3b48622b6e8e91687b34f9e22040c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 31 Jan 2022 12:56:14 +1100 Subject: [PATCH 074/171] Updates to pnl calc --- backtester/backtest/backtest.go | 4 +- .../eventhandlers/portfolio/portfolio.go | 14 +-- .../eventhandlers/portfolio/portfolio_test.go | 6 +- .../portfolio/portfolio_types.go | 2 +- exchanges/order/futures.go | 100 ++++++++++-------- 5 files changed, 63 insertions(+), 63 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 9596be9fcac..b10adc9b75d 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -229,12 +229,12 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu return err } - err = bt.Portfolio.CalculatePNL(ev) + err = bt.Portfolio.UpdateOpenPositionPNL(ev) if err != nil { if errors.Is(err, gctorder.ErrPositionLiquidated) { cr.Liquidate() } else { - log.Errorf(log.BackTester, "CalculatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(log.BackTester, "UpdateOpenPositionPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 4743ef2673b..76486216d0f 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -479,9 +479,9 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat return settings.FuturesTracker.GetPositions(), nil } -// CalculatePNL will analyse any futures orders that have been placed over the backtesting run +// UpdateOpenPositionPNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL -func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { +func (p *Portfolio) UpdateOpenPositionPNL(e common.DataEventHandler) error { if !e.GetAssetType().IsFutures() { return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFutureAsset) } @@ -490,17 +490,11 @@ func (p *Portfolio) CalculatePNL(e common.DataEventHandler) error { return errNoPortfolioSettings } - snapshot, err := p.GetLatestOrderSnapshotForEvent(e) - if err != nil { + _, err := settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(e.GetClosePrice().InexactFloat64(), e.GetTime()) + if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { return err } - for i := range snapshot.Orders { - err = settings.FuturesTracker.TrackNewOrder(snapshot.Orders[i].Order) - if err != nil { - return err - } - } return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index e043ea9b79d..f078f5e1d9f 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -711,7 +711,7 @@ func TestGetLatestSnapshot(t *testing.T) { func TestCalculatePNL(t *testing.T) { p := &Portfolio{} ev := &kline.Kline{} - err := p.CalculatePNL(ev) + err := p.UpdateOpenPositionPNL(ev) if !errors.Is(err, gctorder.ErrNotFutureAsset) { t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFutureAsset) } @@ -736,7 +736,7 @@ func TestCalculatePNL(t *testing.T) { ev.CurrencyPair = pair ev.Time = tt0 - err = p.CalculatePNL(ev) + err = p.UpdateOpenPositionPNL(ev) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -784,7 +784,7 @@ func TestCalculatePNL(t *testing.T) { }, }, }, false) - err = p.CalculatePNL(ev) + err = p.UpdateOpenPositionPNL(ev) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 2da1c42104c..414850fe2ae 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -56,7 +56,7 @@ type Handler interface { GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) GetFee(string, asset.Item, currency.Pair) decimal.Decimal GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) - CalculatePNL(common.DataEventHandler) error + UpdateOpenPositionPNL(common.DataEventHandler) error GetLatestPNLForEvent(handler common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary Reset() diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 2b3c776bd72..b7c8846ff6c 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -116,23 +116,7 @@ func (c *PositionController) UpdateOpenPositionUnrealisedPNL(exch string, item a return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) } - multiPositionTracker.m.Lock() - defer multiPositionTracker.m.Unlock() - pos := multiPositionTracker.positions - if len(pos) == 0 { - return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) - } - latestPos := pos[len(pos)-1] - if latestPos.status != Open { - return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionClosed) - } - err := latestPos.TrackPNLByTime(updated, last) - if err != nil { - return decimal.Zero, fmt.Errorf("%w for position %v %v %v", err, exch, item, pair) - } - latestPos.m.Lock() - defer latestPos.m.Unlock() - return latestPos.unrealisedPNL, nil + return multiPositionTracker.UpdateOpenPositionUnrealisedPNL(last, updated) } // ClearPositionsForExchange resets positions for an @@ -207,50 +191,50 @@ func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPosition } // GetPositions returns all positions -func (e *MultiPositionTracker) GetPositions() []PositionStats { - if e == nil { +func (m *MultiPositionTracker) GetPositions() []PositionStats { + if m == nil { return nil } - e.m.Lock() - defer e.m.Unlock() + m.m.Lock() + defer m.m.Unlock() var resp []PositionStats - for i := range e.positions { - resp = append(resp, e.positions[i].GetStats()) + for i := range m.positions { + resp = append(resp, m.positions[i].GetStats()) } return resp } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices -func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { - if e == nil { +func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { + if m == nil { return common.ErrNilPointer } if d == nil { return ErrSubmissionIsNil } - e.m.Lock() - defer e.m.Unlock() - if d.AssetType != e.asset { + m.m.Lock() + defer m.m.Unlock() + if d.AssetType != m.asset { return errAssetMismatch } - if tracker, ok := e.orderPositions[d.ID]; ok { + if tracker, ok := m.orderPositions[d.ID]; ok { // this has already been associated // update the tracker return tracker.TrackNewOrder(d) } - if len(e.positions) > 0 { - for i := range e.positions { - if e.positions[i].status == Open && i != len(e.positions)-1 { - return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) + if len(m.positions) > 0 { + for i := range m.positions { + if m.positions[i].status == Open && i != len(m.positions)-1 { + return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, m.positions[i], i, len(m.positions)-1) } } - if e.positions[len(e.positions)-1].status == Open { - err := e.positions[len(e.positions)-1].TrackNewOrder(d) + if m.positions[len(m.positions)-1].status == Open { + err := m.positions[len(m.positions)-1].TrackNewOrder(d) if err != nil && !errors.Is(err, ErrPositionClosed) { return err } - e.orderPositions[d.ID] = e.positions[len(e.positions)-1] + m.orderPositions[d.ID] = m.positions[len(m.positions)-1] return nil } } @@ -260,28 +244,28 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { Underlying: d.Pair.Base, Asset: d.AssetType, Side: d.Side, - UseExchangePNLCalculation: e.useExchangePNLCalculations, + UseExchangePNLCalculation: m.useExchangePNLCalculations, } - tracker, err := e.SetupPositionTracker(setup) + tracker, err := m.SetupPositionTracker(setup) if err != nil { return err } - e.positions = append(e.positions, tracker) + m.positions = append(m.positions, tracker) err = tracker.TrackNewOrder(d) if err != nil { return err } - e.orderPositions[d.ID] = tracker + m.orderPositions[d.ID] = tracker return nil } // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed -func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { - if e == nil { +func (m *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { + if m == nil { return nil, common.ErrNilPointer } - if e.exchange == "" { + if m.exchange == "" { return nil, errExchangeNameEmpty } if setup == nil { @@ -295,7 +279,7 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) } resp := &PositionTracker{ - exchange: strings.ToLower(e.exchange), + exchange: strings.ToLower(m.exchange), asset: setup.Asset, contractPair: setup.Pair, underlyingAsset: setup.Underlying, @@ -304,20 +288,42 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) currentDirection: setup.Side, openingDirection: setup.Side, useExchangePNLCalculation: setup.UseExchangePNLCalculation, - offlinePNLCalculation: e.offlinePNLCalculation, + offlinePNLCalculation: m.offlinePNLCalculation, } if !setup.UseExchangePNLCalculation { // use position tracker's pnl calculation by default resp.PNLCalculation = &PNLCalculator{} } else { - if e.exchangePNLCalculation == nil { + if m.exchangePNLCalculation == nil { return nil, ErrNilPNLCalculator } - resp.PNLCalculation = e.exchangePNLCalculation + resp.PNLCalculation = m.exchangePNLCalculation } return resp, nil } +// UpdateOpenPositionUnrealisedPNL updates the pnl for the latest open position +// based on the last price and the time +func (m *MultiPositionTracker) UpdateOpenPositionUnrealisedPNL(last float64, updated time.Time) (decimal.Decimal, error) { + m.m.Lock() + defer m.m.Unlock() + pos := m.positions + if len(pos) == 0 { + return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionsNotLoadedForPair) + } + latestPos := pos[len(pos)-1] + if latestPos.status != Open { + return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionClosed) + } + err := latestPos.TrackPNLByTime(updated, last) + if err != nil { + return decimal.Zero, fmt.Errorf("%w for position %v %v %v", err, m.exchange, m.asset, m.pair) + } + latestPos.m.Lock() + defer latestPos.m.Unlock() + return latestPos.unrealisedPNL, nil +} + // GetStats returns a summary of a future position func (p *PositionTracker) GetStats() PositionStats { if p == nil { From 48666f371efd7e36b0c5015a722fc2f5e09c7af7 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 1 Feb 2022 15:03:06 +1100 Subject: [PATCH 075/171] Tracks pnl for short orders, minor update to strategy --- backtester/backtest/backtest.go | 13 ++- backtester/common/common_types.go | 5 ++ backtester/data/data.go | 4 + backtester/data/data_types.go | 1 + .../eventhandlers/portfolio/portfolio.go | 82 ++++++++++++------- .../portfolio/portfolio_types.go | 5 +- .../ftxcashandcarry/ftxcashandcarry.go | 27 ++++-- 7 files changed, 97 insertions(+), 40 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index b10adc9b75d..48b92987d86 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -229,7 +229,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu return err } - err = bt.Portfolio.UpdateOpenPositionPNL(ev) + err = bt.Portfolio.UpdateOpenPositionPNL(ev, ev.GetClosePrice()) if err != nil { if errors.Is(err, gctorder.ErrPositionLiquidated) { cr.Liquidate() @@ -322,19 +322,28 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if !fde.IsNil() { // some events can only be triggered on a successful fill event bt.EventQueue.AppendEvent(fde) + } + if ev.GetAssetType().IsFutures() { // update collateral holdings exch, err := bt.exchangeManager.GetExchangeByName(ev.GetExchange()) if err != nil { log.Errorf(log.BackTester, "GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } + curr, err := exch.GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(log.BackTester, "GetCollateralCurrencyForContract %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } + err = bt.Portfolio.TrackFuturesOrder(ev.GetOrder()) + if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { + log.Errorf(log.BackTester, "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + err = bt.Portfolio.UpdateOpenPositionPNL(ev, ev.GetClosePrice()) - err = bt.Funding.UpdateCollateral(ev.GetExchange(), fde.GetAssetType(), curr) + err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), curr) if err != nil { log.Errorf(log.BackTester, "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 2923264c99a..8a1bddc2346 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -25,6 +25,11 @@ const ( CouldNotSell order.Side = "COULD NOT SELL" CouldNotShort order.Side = "COULD NOT SHORT" CouldNotLong order.Side = "COULD NOT LONG" + // ClosePosition is used to signal a complete closure + // of any exposure of a position + // This will handle any amount of exposure, no need to calculate how + // much to close + ClosePosition order.Side = "CLOSE POSITION" CouldNotCloseShort order.Side = "COULD NOT CLOSE SHORT" CouldNotCloseLong order.Side = "COULD NOT CLOSE LONG" diff --git a/backtester/data/data.go b/backtester/data/data.go index 61ce3801189..7fdd1dd92b8 100644 --- a/backtester/data/data.go +++ b/backtester/data/data.go @@ -110,6 +110,10 @@ func (b *Base) List() []common.DataEventHandler { return b.stream[b.offset:] } +func (b *Base) IsLastEvent() bool { + return b.latest.GetOffset() == int64(len(b.stream)) +} + // SortStream sorts the stream by timestamp func (b *Base) SortStream() { sort.Slice(b.stream, func(i, j int) bool { diff --git a/backtester/data/data_types.go b/backtester/data/data_types.go index 9ff5a139b7d..c9ec5fb7da5 100644 --- a/backtester/data/data_types.go +++ b/backtester/data/data_types.go @@ -50,6 +50,7 @@ type Streamer interface { History() []common.DataEventHandler Latest() common.DataEventHandler List() []common.DataEventHandler + IsLastEvent() bool Offset() int StreamOpen() []decimal.Decimal diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 76486216d0f..6a21af4cc69 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -3,6 +3,7 @@ package portfolio import ( "errors" "fmt" + "strings" "time" "github.com/shopspring/decimal" @@ -372,30 +373,25 @@ func (p *Portfolio) GetFee(exchangeName string, a asset.Item, cp currency.Pair) } // UpdateHoldings updates the portfolio holdings for the data event -func (p *Portfolio) UpdateHoldings(ev common.DataEventHandler, funds funding.IFundReleaser) error { - if ev == nil { +func (p *Portfolio) UpdateHoldings(e common.DataEventHandler, funds funding.IFundReleaser) error { + if e == nil { return common.ErrNilEvent } if funds == nil { return funding.ErrFundsNotFound } - lookup, ok := p.exchangeAssetPairSettings[ev.GetExchange()][ev.GetAssetType()][ev.Pair()] - if !ok { - return fmt.Errorf("%w for %v %v %v", - errNoPortfolioSettings, - ev.GetExchange(), - ev.GetAssetType(), - ev.Pair()) + settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + if err != nil { + return fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) } - h := lookup.GetLatestHoldings() - var err error + h := settings.GetLatestHoldings() if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, funds) + h, err = holdings.Create(e, funds) if err != nil { return err } } - h.UpdateValue(ev) + h.UpdateValue(e) err = p.setHoldingsForOffset(&h, true) if errors.Is(err, errNoHoldings) { err = p.setHoldingsForOffset(&h, false) @@ -465,13 +461,9 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat if !e.GetAssetType().IsFutures() { return nil, errors.New("not a future") } - settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] - if !ok { - return nil, fmt.Errorf("%v %v %v %w", - e.GetExchange(), - e.GetAssetType(), - e.Pair(), - errNoPortfolioSettings) + settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + if err != nil { + return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) } if settings.FuturesTracker == nil { return nil, errors.New("no futures tracker") @@ -481,16 +473,16 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat // UpdateOpenPositionPNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL -func (p *Portfolio) UpdateOpenPositionPNL(e common.DataEventHandler) error { +func (p *Portfolio) UpdateOpenPositionPNL(e common.EventHandler, closePrice decimal.Decimal) error { if !e.GetAssetType().IsFutures() { return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFutureAsset) } - settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] - if !ok { - return errNoPortfolioSettings + settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + if err != nil { + return fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) } - _, err := settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(e.GetClosePrice().InexactFloat64(), e.GetTime()) + _, err = settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(closePrice.InexactFloat64(), e.GetTime()) if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { return err } @@ -498,15 +490,32 @@ func (p *Portfolio) UpdateOpenPositionPNL(e common.DataEventHandler) error { return nil } +// TrackFuturesOrder updates the futures tracker with a new order +// from a fill event +func (p *Portfolio) TrackFuturesOrder(detail *gctorder.Detail) error { + if detail == nil { + return gctorder.ErrSubmissionIsNil + } + if !detail.AssetType.IsFutures() { + return fmt.Errorf("order '%v' %w", detail.ID, gctorder.ErrNotFuturesAsset) + } + settings, err := p.getSettings(detail.Exchange, detail.AssetType, detail.Pair) + if err != nil { + return fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) + } + + return settings.FuturesTracker.TrackNewOrder(detail) +} + // GetLatestPNLForEvent takes in an event and returns the latest PNL data // if it exists func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, error) { if !e.GetAssetType().IsFutures() { return nil, errors.New("not a future") } - settings, ok := p.exchangeAssetPairSettings[e.GetExchange()][e.GetAssetType()][e.Pair()] - if !ok { - return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), errNoPortfolioSettings) + settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + if err != nil { + return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) } if settings.FuturesTracker == nil { @@ -530,6 +539,23 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er return response, nil } +func (p *Portfolio) getSettings(exch string, item asset.Item, pair currency.Pair) (*Settings, error) { + exchMap, ok := p.exchangeAssetPairSettings[strings.ToLower(exch)] + if !ok { + return nil, errExchangeUnset + } + itemMap, ok := exchMap[item] + if !ok { + return nil, errAssetUnset + } + pairMap, ok := itemMap[pair] + if !ok { + return nil, errCurrencyPairUnset + } + + return pairMap, nil +} + // GetLatestPNLs returns all PNL details in one array func (p *Portfolio) GetLatestPNLs() []PNLSummary { var result []PNLSummary diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 414850fe2ae..2ad629fa5fa 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -56,8 +56,9 @@ type Handler interface { GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) GetFee(string, asset.Item, currency.Pair) decimal.Decimal GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) - UpdateOpenPositionPNL(common.DataEventHandler) error - GetLatestPNLForEvent(handler common.EventHandler) (*PNLSummary, error) + TrackFuturesOrder(*gctorder.Detail) error + UpdateOpenPositionPNL(common.EventHandler, decimal.Decimal) error + GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary Reset() } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 1967c1acfbe..c69fab60647 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" @@ -100,15 +101,17 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if err != nil { return nil, err } + spotSignal, err := s.GetBaseData(v.spotSignal) + if err != nil { + return nil, err + } + futuresSignal, err := s.GetBaseData(v.futureSignal) + if err != nil { + return nil, err + } if len(pos) == 0 || pos[len(pos)-1].Status == order.Closed { - spotSignal, err := s.GetBaseData(v.spotSignal) - if err != nil { - return nil, err - } - futuresSignal, err := s.GetBaseData(v.futureSignal) - if err != nil { - return nil, err - } + // check to see if order is appropriate to action + spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) spotSignal.AppendReason(fmt.Sprintf("signalling purchase of %v", spotSignal.Pair())) // first the spot purchase @@ -127,6 +130,14 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf } else { // analyse the conditions of the order to monitor whether // its worthwhile to ever close the short + if v.futureSignal.IsLastEvent() { + futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.AppendReason("closing position on last event") + futuresSignal.SetDirection(order.Long) + response = append(response, &futuresSignal) + } + // determine how close future price is to spot price + // compare it to see if it meets the % value threshold and close position } } return response, nil From e1260a2b1a928e20ec81eb3f34c3d576dffea479 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 4 Feb 2022 16:09:53 +1100 Subject: [PATCH 076/171] Close and open event based on conditions --- .../ftxcashandcarry/ftxcashandcarry.go | 136 +++++++++++------- .../ftxcashandcarry/ftxcashandcarry_types.go | 15 +- 2 files changed, 92 insertions(+), 59 deletions(-) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index c69fab60647..b9a30b435ac 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" @@ -48,45 +49,6 @@ type cashCarrySignals struct { var errNotSetup = errors.New("sent incomplete signals") -func sortSignals(d []data.Handler, f funding.IFundTransferer) (map[currency.Pair]cashCarrySignals, error) { - var response = make(map[currency.Pair]cashCarrySignals) - for i := range d { - l := d[i].Latest() - if !strings.EqualFold(l.GetExchange(), exchangeName) { - return nil, fmt.Errorf("%w, received '%v'", errOnlyFTXSupported, l.GetExchange()) - } - a := l.GetAssetType() - switch { - case a == asset.Spot: - entry := response[l.Pair().Format("", false)] - entry.spotSignal = d[i] - response[l.Pair().Format("", false)] = entry - case a.IsFutures(): - u, err := l.GetUnderlyingPair() - if err != nil { - return nil, err - } - entry := response[u.Format("", false)] - entry.futureSignal = d[i] - response[u.Format("", false)] = entry - default: - return nil, errFuturesOnly - } - } - // validate that each set of signals is matched - for _, v := range response { - if v.futureSignal == nil { - return nil, errNotSetup - - } - if v.spotSignal == nil { - return nil, errNotSetup - } - } - - return response, nil -} - // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransferer, p portfolio.Handler) ([]signal.Event, error) { @@ -109,9 +71,12 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if err != nil { return nil, err } - if len(pos) == 0 || pos[len(pos)-1].Status == order.Closed { - // check to see if order is appropriate to action + fp := v.futureSignal.Latest().GetClosePrice() + sp := v.spotSignal.Latest().GetClosePrice() + switch { + case len(pos) == 0: + // check to see if order is appropriate to action spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) spotSignal.AppendReason(fmt.Sprintf("signalling purchase of %v", spotSignal.Pair())) // first the spot purchase @@ -127,26 +92,95 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf // to use as collateral spotSignal.FillDependentEvent = &futuresSignal response = append(response, &spotSignal) - } else { - // analyse the conditions of the order to monitor whether - // its worthwhile to ever close the short - if v.futureSignal.IsLastEvent() { + case len(pos) > 0 && v.futureSignal.IsLastEvent(): + futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.AppendReason("closing position on last event") + futuresSignal.SetDirection(order.Long) + response = append(response, &futuresSignal) + case len(pos) > 0 && pos[len(pos)-1].Status == order.Open: + if fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage) { futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReason("closing position on last event") + futuresSignal.AppendReason("closing position after reaching close short distance percentage") futuresSignal.SetDirection(order.Long) response = append(response, &futuresSignal) } - // determine how close future price is to spot price - // compare it to see if it meets the % value threshold and close position + case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed: + if fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage) { + futuresSignal.SetDirection(order.Short) + futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) + futuresSignal.AppendReason("opening position after reaching open short distance percentage") + response = append(response, &futuresSignal) + } + } + } + return response, nil +} + +func sortSignals(d []data.Handler, f funding.IFundTransferer) (map[currency.Pair]cashCarrySignals, error) { + var response = make(map[currency.Pair]cashCarrySignals) + for i := range d { + l := d[i].Latest() + if !strings.EqualFold(l.GetExchange(), exchangeName) { + return nil, fmt.Errorf("%w, received '%v'", errOnlyFTXSupported, l.GetExchange()) + } + a := l.GetAssetType() + switch { + case a == asset.Spot: + entry := response[l.Pair().Format("", false)] + entry.spotSignal = d[i] + response[l.Pair().Format("", false)] = entry + case a.IsFutures(): + u, err := l.GetUnderlyingPair() + if err != nil { + return nil, err + } + entry := response[u.Format("", false)] + entry.futureSignal = d[i] + response[u.Format("", false)] = entry + default: + return nil, errFuturesOnly + } + } + // validate that each set of signals is matched + for _, v := range response { + if v.futureSignal == nil { + return nil, errNotSetup + + } + if v.spotSignal == nil { + return nil, errNotSetup } } + return response, nil } // SetCustomSettings not required for DCA -func (s *Strategy) SetCustomSettings(_ map[string]interface{}) error { - return base.ErrCustomSettingsUnsupported +func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) error { + for k, v := range customSettings { + switch k { + case openShortDistancePercentageString: + rsiHigh, ok := v.(float64) + if !ok || rsiHigh <= 0 { + return fmt.Errorf("%w provided rsi-high value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.openShortDistancePercentage = decimal.NewFromFloat(rsiHigh) + case closeShortDistancePercentageString: + rsiLow, ok := v.(float64) + if !ok || rsiLow <= 0 { + return fmt.Errorf("%w provided rsi-low value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + } + s.closeShortDistancePercentage = decimal.NewFromFloat(rsiLow) + default: + return fmt.Errorf("%w unrecognised custom setting key %v with value %v. Cannot apply", base.ErrInvalidCustomSettings, k, v) + } + } + + return nil } // SetDefaults not required for DCA -func (s *Strategy) SetDefaults() {} +func (s *Strategy) SetDefaults() { + s.closeShortDistancePercentage = decimal.NewFromInt(5) + s.closeShortDistancePercentage = decimal.NewFromInt(5) +} diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go index c2da213ea51..d2e72e29833 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go @@ -9,11 +9,11 @@ import ( const ( // Name is the strategy name - Name = "ftx-cash-carry" - description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` - exchangeName = "ftx" - spotDifferenceEntry = "spot-difference-entry" - spotDifferenceExit = "spot-difference-exit" + Name = "ftx-cash-carry" + description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` + exchangeName = "ftx" + openShortDistancePercentageString = "openShortDistancePercentage" + closeShortDistancePercentageString = "closeShortDistancePercentage" ) var ( @@ -24,7 +24,6 @@ var ( // Strategy is an implementation of the Handler interface type Strategy struct { base.Strategy - rsiPeriod decimal.Decimal - rsiLow decimal.Decimal - rsiHigh decimal.Decimal + openShortDistancePercentage decimal.Decimal + closeShortDistancePercentage decimal.Decimal } From cac4dd82a86140af1e70d522f4abe0fea57a6532 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 11 Feb 2022 16:01:58 +1100 Subject: [PATCH 077/171] Adds pnl data for currency statistics --- backtester/backtest/backtest.go | 16 ++++++++-- .../eventhandlers/portfolio/portfolio.go | 1 + .../portfolio/portfolio_types.go | 1 + .../statistics/currencystatistics.go | 10 +++++++ .../statistics/currencystatistics_test.go | 14 ++++----- .../eventhandlers/statistics/statistics.go | 29 +++++++++++++++---- .../statistics/statistics_types.go | 13 +++++++-- 7 files changed, 66 insertions(+), 18 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 48b92987d86..af6f0783517 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -342,13 +342,25 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) return } err = bt.Portfolio.UpdateOpenPositionPNL(ev, ev.GetClosePrice()) - + if err != nil { + log.Errorf(log.BackTester, "UpdateOpenPositionPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), curr) if err != nil { log.Errorf(log.BackTester, "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + pnl, err := bt.Portfolio.GetLatestPNLForEvent(ev) + if err != nil { + log.Errorf(log.BackTester, "GetLatestPNLForEvent %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + err = bt.Statistic.AddPNLForTime(pnl) + if err != nil { + log.Errorf(log.BackTester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } - } // RunLive is a proof of concept function that does not yet support multi currency usage diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 6a21af4cc69..7e9c7123a59 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -526,6 +526,7 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er Exchange: e.GetExchange(), Item: e.GetAssetType(), Pair: e.Pair(), + Offset: e.GetOffset(), } positions := settings.FuturesTracker.GetPositions() if len(positions) == 0 { diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 2ad629fa5fa..5f9f1b31133 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -87,5 +87,6 @@ type PNLSummary struct { Exchange string Item asset.Item Pair currency.Pair + Offset int64 PNL gctorder.PNLResult } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index b854ba63594..9f9e2767c64 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -116,6 +116,11 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e c.TotalValueLost = last.Holdings.TotalValueLost.Round(2) c.TotalValueLostToSlippage = last.Holdings.TotalValueLostToSlippage.Round(2) c.TotalAssetValue = last.Holdings.BaseValue.Round(8) + if last.PNL != nil { + c.UnrealisedPNL = last.PNL.PNL.UnrealisedPNL + // ???? + c.RealisedPNL = last.PNL.PNL.RealisedPNLBeforeFees + } if len(errs) > 0 { return errs } @@ -202,6 +207,11 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. log.Infof(log.BackTester, "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) log.Infof(log.BackTester, "%s Final total value: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) } + + if last.PNL != nil { + log.Infof(log.BackTester, "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.PNL.UnrealisedPNL, 8, ".", ",")) + log.Infof(log.BackTester, "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.PNL.RealisedPNLBeforeFees, 8, ".", ",")) + } if len(errs) > 0 { log.Info(log.BackTester, "------------------Errors-------------------------------------") for i := range errs { diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 57efdcd47d6..5586d18444b 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -32,7 +32,7 @@ func TestCalculateResults(t *testing.T) { CurrencyPair: p, AssetType: a, } - ev := EventStore{ + ev := DataAtOffset{ Holdings: holdings.Holding{ ChangeInTotalValuePercent: decimal.NewFromFloat(0.1333), Timestamp: tt1, @@ -71,7 +71,7 @@ func TestCalculateResults(t *testing.T) { } even2 := even even2.Time = tt2 - ev2 := EventStore{ + ev2 := DataAtOffset{ Holdings: holdings.Holding{ ChangeInTotalValuePercent: decimal.NewFromFloat(0.1337), Timestamp: tt2, @@ -169,7 +169,7 @@ func TestPrintResults(t *testing.T) { CurrencyPair: p, AssetType: a, } - ev := EventStore{ + ev := DataAtOffset{ Holdings: holdings.Holding{ ChangeInTotalValuePercent: decimal.NewFromFloat(0.1333), Timestamp: tt1, @@ -208,7 +208,7 @@ func TestPrintResults(t *testing.T) { } even2 := even even2.Time = tt2 - ev2 := EventStore{ + ev2 := DataAtOffset{ Holdings: holdings.Holding{ ChangeInTotalValuePercent: decimal.NewFromFloat(0.1337), Timestamp: tt2, @@ -261,9 +261,9 @@ func TestCalculateHighestCommittedFunds(t *testing.T) { tt2 := time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC) tt3 := time.Date(2021, 3, 1, 0, 0, 0, 0, time.UTC) c.Events = append(c.Events, - EventStore{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1337)}, Holdings: holdings.Holding{Timestamp: tt1, BaseSize: decimal.NewFromInt(10)}}, - EventStore{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1338)}, Holdings: holdings.Holding{Timestamp: tt2, BaseSize: decimal.NewFromInt(1337)}}, - EventStore{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1339)}, Holdings: holdings.Holding{Timestamp: tt3, BaseSize: decimal.NewFromInt(11)}}, + DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1337)}, Holdings: holdings.Holding{Timestamp: tt1, BaseSize: decimal.NewFromInt(10)}}, + DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1338)}, Holdings: holdings.Holding{Timestamp: tt2, BaseSize: decimal.NewFromInt(1337)}}, + DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1339)}, Holdings: holdings.Holding{Timestamp: tt3, BaseSize: decimal.NewFromInt(11)}}, ) c.calculateHighestCommittedFunds() if c.HighestCommittedFunds.Time != tt2 { diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 6f8c990f4a6..09d4a4446ea 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -9,6 +9,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" @@ -50,7 +51,7 @@ func (s *Statistic) SetupEventForTime(ev common.DataEventHandler) error { } } lookup.Events = append(lookup.Events, - EventStore{ + DataAtOffset{ DataEvent: ev, }, ) @@ -124,10 +125,27 @@ func (s *Statistic) AddHoldingsForTime(h *holdings.Holding) error { for i := len(lookup.Events) - 1; i >= 0; i-- { if lookup.Events[i].DataEvent.GetOffset() == h.Offset { lookup.Events[i].Holdings = *h - break + return nil } } - return nil + return fmt.Errorf("%v %v %v %w %v", h.Exchange, h.Asset, h.Pair, errNoDataAtOffset, h.Offset) +} + +func (s *Statistic) AddPNLForTime(pnl *portfolio.PNLSummary) error { + if s.ExchangeAssetPairStatistics == nil { + return errExchangeAssetPairStatsUnset + } + lookup := s.ExchangeAssetPairStatistics[pnl.Exchange][pnl.Item][pnl.Pair] + if lookup == nil { + return fmt.Errorf("%w for %v %v %v to set pnl", errCurrencyStatisticsUnset, pnl.Exchange, pnl.Item, pnl.Pair) + } + for i := len(lookup.Events) - 1; i >= 0; i-- { + if lookup.Events[i].DataEvent.GetOffset() == pnl.Offset { + lookup.Events[i].PNL = pnl + return nil + } + } + return fmt.Errorf("%v %v %v %w %v", pnl.Exchange, pnl.Item, pnl.Pair, errNoDataAtOffset, pnl.Offset) } // AddComplianceSnapshotForTime adds the compliance snapshot to the statistics at the time period @@ -148,11 +166,10 @@ func (s *Statistic) AddComplianceSnapshotForTime(c compliance.Snapshot, e fill.E for i := len(lookup.Events) - 1; i >= 0; i-- { if lookup.Events[i].DataEvent.GetOffset() == e.GetOffset() { lookup.Events[i].Transactions = c - break + return nil } } - - return nil + return fmt.Errorf("%v %v %v %w %v", e.GetExchange(), e.GetAssetType(), e.Pair(), errNoDataAtOffset, e.GetOffset()) } // CalculateAllResults calculates the statistics of all exchange asset pair holdings, diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 72a7988d9e1..c4962354986 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -6,6 +6,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" @@ -26,6 +27,7 @@ var ( errMissingSnapshots = errors.New("funding report item missing USD snapshots") errNoRelevantStatsFound = errors.New("no relevant currency pair statistics found") errReceivedNoData = errors.New("received no data") + errNoDataAtOffset = errors.New("no data found at offset") ) // Statistic holds all statistical information for a backtester run, from drawdowns to ratios. @@ -72,6 +74,7 @@ type Handler interface { CalculateAllResults() error Reset() Serialise() (string, error) + AddPNLForTime(*portfolio.PNLSummary) error } // Results holds some statistics on results @@ -113,15 +116,17 @@ type CurrencyStats interface { SortinoRatio(decimal.Decimal) decimal.Decimal } -// EventStore is used to hold all event information +// DataAtOffset is used to hold all event information // at a time interval -type EventStore struct { +type DataAtOffset struct { Holdings holdings.Holding Transactions compliance.Snapshot DataEvent common.DataEventHandler SignalEvent signal.Event OrderEvent order.Event FillEvent fill.Event + // TODO: consider moving this to an interface so we aren't tied to this summary type + PNL *portfolio.PNLSummary } // CurrencyPairStatistic Holds all events and statistics relevant to an exchange, asset type and currency pair @@ -140,6 +145,8 @@ type CurrencyPairStatistic struct { HighestClosePrice decimal.Decimal `json:"highest-close-price"` MarketMovement decimal.Decimal `json:"market-movement"` StrategyMovement decimal.Decimal `json:"strategy-movement"` + UnrealisedPNL decimal.Decimal `json:"unrealised-pnl"` + RealisedPNL decimal.Decimal `json:"realised-pnl"` CompoundAnnualGrowthRate decimal.Decimal `json:"compound-annual-growth-rate"` TotalAssetValue decimal.Decimal TotalFees decimal.Decimal @@ -147,7 +154,7 @@ type CurrencyPairStatistic struct { TotalValueLostToSlippage decimal.Decimal TotalValueLost decimal.Decimal - Events []EventStore `json:"-"` + Events []DataAtOffset `json:"-"` MaxDrawdown Swing `json:"max-drawdown,omitempty"` HighestCommittedFunds ValueAtTime `json:"highest-committed-funds"` From b917606209e408c788c97d09657cb88a210cfdc1 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 14 Feb 2022 17:33:12 +1100 Subject: [PATCH 078/171] Working through PNL calculation automatically. Now panics --- backtester/backtest/backtest.go | 31 ++++++++--------- backtester/eventhandlers/exchange/exchange.go | 4 +++ .../eventhandlers/portfolio/portfolio.go | 34 +++++++++---------- .../eventhandlers/portfolio/portfolio_test.go | 6 ++-- .../portfolio/portfolio_types.go | 2 +- backtester/funding/funding.go | 11 ++++-- backtester/funding/funding_types.go | 2 +- exchanges/order/futures.go | 14 +++++--- exchanges/order/futures_test.go | 6 ++-- 9 files changed, 60 insertions(+), 50 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index af6f0783517..8c0bb23b76e 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -104,6 +104,15 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { if err != nil { return err } + + if ev.GetAssetType().IsFutures() { + // hardcoded fix + err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) + if err != nil { + return err + } + } + switch eType := ev.(type) { case common.DataEventHandler: if bt.Strategy.UsingSimultaneousProcessing() { @@ -229,12 +238,12 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu return err } - err = bt.Portfolio.UpdateOpenPositionPNL(ev, ev.GetClosePrice()) + err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice()) if err != nil { if errors.Is(err, gctorder.ErrPositionLiquidated) { cr.Liquidate() } else { - log.Errorf(log.BackTester, "UpdateOpenPositionPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(log.BackTester, "UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } } @@ -324,29 +333,17 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) bt.EventQueue.AppendEvent(fde) } if ev.GetAssetType().IsFutures() { - // update collateral holdings - exch, err := bt.exchangeManager.GetExchangeByName(ev.GetExchange()) - if err != nil { - log.Errorf(log.BackTester, "GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return - } - - curr, err := exch.GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) - if err != nil { - log.Errorf(log.BackTester, "GetCollateralCurrencyForContract %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return - } err = bt.Portfolio.TrackFuturesOrder(ev.GetOrder()) if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { log.Errorf(log.BackTester, "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } - err = bt.Portfolio.UpdateOpenPositionPNL(ev, ev.GetClosePrice()) + err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice()) if err != nil { - log.Errorf(log.BackTester, "UpdateOpenPositionPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(log.BackTester, "UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } - err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), curr) + err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(log.BackTester, "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 15cab8304c5..3ddba0464ab 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -82,6 +82,10 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.SetDirection(common.CouldNotBuy) case gctorder.Sell: f.SetDirection(common.CouldNotSell) + case gctorder.Short: + f.SetDirection(common.CouldNotShort) + case gctorder.Long: + f.SetDirection(common.CouldNotLong) default: f.SetDirection(common.DoNothing) } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 7e9c7123a59..1372d36d1c3 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -140,6 +140,10 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, siz case gctorder.Sell: originalOrderSignal.Direction = common.CouldNotSell case common.CouldNotBuy, common.CouldNotSell: + case gctorder.Short: + originalOrderSignal.Direction = common.CouldNotShort + case gctorder.Long: + originalOrderSignal.Direction = common.CouldNotLong default: originalOrderSignal.Direction = common.DoNothing } @@ -249,18 +253,6 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (fill.E log.Error(log.BackTester, err) } ev.SetExchangeFee(decimal.Zero) - direction := ev.GetDirection() - if direction == common.DoNothing || - direction == common.CouldNotBuy || - direction == common.CouldNotSell || - direction == common.MissingData || - direction == common.CouldNotCloseLong || - direction == common.CouldNotCloseShort || - direction == common.CouldNotLong || - direction == common.CouldNotShort || - direction == "" { - return ev, nil - } return ev, nil } @@ -471,9 +463,9 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat return settings.FuturesTracker.GetPositions(), nil } -// UpdateOpenPositionPNL will analyse any futures orders that have been placed over the backtesting run +// UpdatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL -func (p *Portfolio) UpdateOpenPositionPNL(e common.EventHandler, closePrice decimal.Decimal) error { +func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) error { if !e.GetAssetType().IsFutures() { return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFutureAsset) } @@ -482,9 +474,17 @@ func (p *Portfolio) UpdateOpenPositionPNL(e common.EventHandler, closePrice deci return fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) } - _, err = settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(closePrice.InexactFloat64(), e.GetTime()) - if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { - return err + pos := settings.FuturesTracker.GetPositions() + if len(pos) == 0 { + // idk + } + if pos[len(pos)-1].Status == gctorder.Closed { + gctorder.CalculateRealisedPNL(pos[len(pos)-1].PNLHistory) + } else { + _, err = settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(closePrice.InexactFloat64(), e.GetTime()) + if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { + return err + } } return nil diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index f078f5e1d9f..419b9fb7c69 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -711,7 +711,7 @@ func TestGetLatestSnapshot(t *testing.T) { func TestCalculatePNL(t *testing.T) { p := &Portfolio{} ev := &kline.Kline{} - err := p.UpdateOpenPositionPNL(ev) + err := p.UpdatePNL(ev) if !errors.Is(err, gctorder.ErrNotFutureAsset) { t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFutureAsset) } @@ -736,7 +736,7 @@ func TestCalculatePNL(t *testing.T) { ev.CurrencyPair = pair ev.Time = tt0 - err = p.UpdateOpenPositionPNL(ev) + err = p.UpdatePNL(ev) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -784,7 +784,7 @@ func TestCalculatePNL(t *testing.T) { }, }, }, false) - err = p.UpdateOpenPositionPNL(ev) + err = p.UpdatePNL(ev) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 5f9f1b31133..dfb739d7d41 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -57,7 +57,7 @@ type Handler interface { GetFee(string, asset.Item, currency.Pair) decimal.Decimal GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) TrackFuturesOrder(*gctorder.Detail) error - UpdateOpenPositionPNL(common.EventHandler, decimal.Decimal) error + UpdatePNL(common.EventHandler, decimal.Decimal) error GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary Reset() diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 079c2561d7f..24bafb93129 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -474,7 +474,7 @@ func (f *FundManager) GetAllFunding() []BasicItem { return result } -func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, collateralCurrency currency.Code) error { +func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, pair currency.Pair) error { exchMap := make(map[string]exchange.IBotExchange) var collateralAmount decimal.Decimal var err error @@ -515,13 +515,18 @@ func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, collate collateralAmount = collateralAmount.Add(latest) } + collat, err := exchMap[exchName].GetCollateralCurrencyForContract(item, pair) + if err != nil { + return err + } + for i := range f.items { if f.items[i].exchange == exchName && f.items[i].asset == item && - f.items[i].currency.Match(collateralCurrency) { + f.items[i].currency.Match(collat) { f.items[i].available = collateralAmount return nil } } - return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, exchName, item, collateralCurrency) + return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, exchName, item, collat) } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index d0c61396ff1..44e49b76a8c 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -35,7 +35,7 @@ type IFundingManager interface { USDTrackingDisabled() bool LiquidateByCollateral(currency.Code) error GetAllFunding() []BasicItem - UpdateCollateral(string, asset.Item, currency.Code) error + UpdateCollateral(string, asset.Item, currency.Pair) error } // IFundingReader is a simple interface of diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b7c8846ff6c..44decb0b5e7 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -334,6 +334,10 @@ func (p *PositionTracker) GetStats() PositionStats { var orders []Detail orders = append(orders, p.longPositions...) orders = append(orders, p.shortPositions...) + rPNL := p.realisedPNL + if p.status == Closed { + rPNL = CalculateRealisedPNL(p.pnlHistory) + } return PositionStats{ Exchange: p.exchange, Asset: p.asset, @@ -341,7 +345,7 @@ func (p *PositionTracker) GetStats() PositionStats { Underlying: p.underlyingAsset, Status: p.status, Orders: orders, - RealisedPNL: p.realisedPNL, + RealisedPNL: rPNL, UnrealisedPNL: p.unrealisedPNL, Direction: p.currentDirection, OpeningDirection: p.openingDirection, @@ -393,7 +397,7 @@ func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { } p.m.Lock() defer p.m.Unlock() - return calculateRealisedPNL(p.pnlHistory) + return CalculateRealisedPNL(p.pnlHistory) } // GetLatestPNLSnapshot takes the latest pnl history value @@ -578,7 +582,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) - p.realisedPNL = calculateRealisedPNL(p.pnlHistory) + p.realisedPNL = CalculateRealisedPNL(p.pnlHistory) p.unrealisedPNL = decimal.Zero } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { @@ -651,9 +655,9 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque return response, nil } -// calculateRealisedPNL calculates the total realised PNL +// CalculateRealisedPNL calculates the total realised PNL // based on PNL history, minus fees -func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { +func CalculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 5bcf6c2a252..ff1f7d4e057 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -597,11 +597,11 @@ func TestClearPositionsForExchange(t *testing.T) { func TestCalculateRealisedPNL(t *testing.T) { t.Parallel() - result := calculateRealisedPNL(nil) + result := CalculateRealisedPNL(nil) if !result.IsZero() { t.Error("expected zero") } - result = calculateRealisedPNL([]PNLResult{ + result = CalculateRealisedPNL([]PNLResult{ { RealisedPNLBeforeFees: decimal.NewFromInt(1337), }, @@ -610,7 +610,7 @@ func TestCalculateRealisedPNL(t *testing.T) { t.Error("expected 1337") } - result = calculateRealisedPNL([]PNLResult{ + result = CalculateRealisedPNL([]PNLResult{ { RealisedPNLBeforeFees: decimal.NewFromInt(1339), Fee: decimal.NewFromInt(2), From f2bd71f5cb732c3be805bd6829258029130f239b Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 15 Feb 2022 15:45:50 +1100 Subject: [PATCH 079/171] Adds tracking, is blocked from design --- backtester/backtest/backtest.go | 2 +- .../eventhandlers/portfolio/portfolio.go | 39 ++++++++++++------- .../portfolio/portfolio_types.go | 2 +- backtester/funding/collateral.go | 2 +- backtester/funding/funding_types.go | 2 +- exchanges/order/futures.go | 5 ++- exchanges/order/futures_types.go | 5 ++- 7 files changed, 38 insertions(+), 19 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index 8c0bb23b76e..ab63ffb8508 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -333,7 +333,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) bt.EventQueue.AppendEvent(fde) } if ev.GetAssetType().IsFutures() { - err = bt.Portfolio.TrackFuturesOrder(ev.GetOrder()) + err = bt.Portfolio.TrackFuturesOrder(ev.GetOrder(), funds) if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { log.Errorf(log.BackTester, "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 1372d36d1c3..752e28f2b4f 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -474,17 +474,9 @@ func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) return fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) } - pos := settings.FuturesTracker.GetPositions() - if len(pos) == 0 { - // idk - } - if pos[len(pos)-1].Status == gctorder.Closed { - gctorder.CalculateRealisedPNL(pos[len(pos)-1].PNLHistory) - } else { - _, err = settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(closePrice.InexactFloat64(), e.GetTime()) - if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { - return err - } + _, err = settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(closePrice.InexactFloat64(), e.GetTime()) + if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { + return err } return nil @@ -492,19 +484,40 @@ func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) // TrackFuturesOrder updates the futures tracker with a new order // from a fill event -func (p *Portfolio) TrackFuturesOrder(detail *gctorder.Detail) error { +func (p *Portfolio) TrackFuturesOrder(detail *gctorder.Detail, fund funding.IFundReleaser) error { if detail == nil { return gctorder.ErrSubmissionIsNil } if !detail.AssetType.IsFutures() { return fmt.Errorf("order '%v' %w", detail.ID, gctorder.ErrNotFuturesAsset) } + collateralReleaser, err := fund.GetCollateralReleaser() + if err != nil { + return fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) + } settings, err := p.getSettings(detail.Exchange, detail.AssetType, detail.Pair) if err != nil { return fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) } - return settings.FuturesTracker.TrackNewOrder(detail) + err = settings.FuturesTracker.TrackNewOrder(detail) + if err != nil { + return err + } + + pos := settings.FuturesTracker.GetPositions() + if len(pos) == 0 { + return fmt.Errorf("%w should not happen", errNoHoldings) + } + if pos[len(pos)-1].Status == gctorder.Closed { + amount := decimal.NewFromFloat(detail.Amount) + err = collateralReleaser.TakeProfit(amount, pos[len(pos)-1].EntryAmount, pos[len(pos)-1].RealisedPNL) + if err != nil { + return err + } + } + + return nil } // GetLatestPNLForEvent takes in an event and returns the latest PNL data diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index dfb739d7d41..7e3b5ecbb1b 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -56,7 +56,7 @@ type Handler interface { GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) GetFee(string, asset.Item, currency.Pair) decimal.Decimal GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) - TrackFuturesOrder(*gctorder.Detail) error + TrackFuturesOrder(*gctorder.Detail, funding.IFundReleaser) error UpdatePNL(common.EventHandler, decimal.Decimal) error GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 86cef1f9cee..5d50d2b76c8 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -62,7 +62,7 @@ func (c *Collateral) Reserve(amount decimal.Decimal, _ order.Side) error { func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { // turn this into a protected func - c.Contract.available = c.Contract.available.Add(amount) + c.Contract.available = c.Contract.available.Sub(amount) return nil } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 44e49b76a8c..4ef50248fff 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -116,7 +116,7 @@ type IPairReleaser interface { type ICollateralReleaser interface { ICollateralReader - TakeProfit(decimal.Decimal, decimal.Decimal, decimal.Decimal) error + TakeProfit(contracts, originalPositionSize, positionReturns decimal.Decimal) error ReleaseContracts(decimal.Decimal) error Liquidate() } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 44decb0b5e7..8f99db234bf 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -241,6 +241,7 @@ func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { setup := &PositionTrackerSetup{ Pair: d.Pair, EntryPrice: decimal.NewFromFloat(d.Price), + EntryAmount: decimal.NewFromFloat(d.Amount), Underlying: d.Pair.Base, Asset: d.AssetType, Side: d.Side, @@ -285,6 +286,7 @@ func (m *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) underlyingAsset: setup.Underlying, status: Open, entryPrice: setup.EntryPrice, + entryAmount: setup.EntryAmount, currentDirection: setup.Side, openingDirection: setup.Side, useExchangePNLCalculation: setup.UseExchangePNLCalculation, @@ -349,7 +351,8 @@ func (p *PositionTracker) GetStats() PositionStats { UnrealisedPNL: p.unrealisedPNL, Direction: p.currentDirection, OpeningDirection: p.openingDirection, - OpeningPrice: p.entryPrice, + EntryPrice: p.entryPrice, + EntryAmount: p.entryAmount, Price: p.latestPrice, Exposure: p.exposure, PNLHistory: p.pnlHistory, diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 6bf9f0cefca..8db769d9c54 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -136,6 +136,7 @@ type PositionTracker struct { exposure decimal.Decimal currentDirection Side openingDirection Side + entryAmount decimal.Decimal status Status unrealisedPNL decimal.Decimal realisedPNL decimal.Decimal @@ -155,6 +156,7 @@ type PositionTracker struct { type PositionTrackerSetup struct { Pair currency.Pair EntryPrice decimal.Decimal + EntryAmount decimal.Decimal Underlying currency.Code Asset asset.Item Side Side @@ -230,7 +232,8 @@ type PositionStats struct { Exposure decimal.Decimal OpeningDirection Side - OpeningPrice decimal.Decimal + EntryAmount decimal.Decimal + EntryPrice decimal.Decimal PNLHistory []PNLResult } From 971ebc938462194c21bcaf5d5410096e8845a39e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 16 Feb 2022 15:54:25 +1100 Subject: [PATCH 080/171] Work to flesh out closing a position --- backtester/backtest/backtest.go | 28 +++----- .../config/examples/ftx-cash-carry.strat | 2 +- backtester/eventhandlers/exchange/exchange.go | 66 ++++++++----------- .../eventhandlers/portfolio/portfolio.go | 60 ++++++++++++----- .../portfolio/portfolio_types.go | 2 +- .../eventhandlers/portfolio/size/size.go | 2 + .../eventhandlers/statistics/statistics.go | 3 + .../ftxcashandcarry/ftxcashandcarry.go | 2 - backtester/eventtypes/order/order.go | 4 ++ backtester/eventtypes/order/order_types.go | 2 + 10 files changed, 94 insertions(+), 77 deletions(-) diff --git a/backtester/backtest/backtest.go b/backtester/backtest/backtest.go index ab63ffb8508..e20b2dff0db 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/backtest/backtest.go @@ -333,30 +333,22 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) bt.EventQueue.AppendEvent(fde) } if ev.GetAssetType().IsFutures() { - err = bt.Portfolio.TrackFuturesOrder(ev.GetOrder(), funds) - if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { - log.Errorf(log.BackTester, "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return - } - err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice()) - if err != nil { - log.Errorf(log.BackTester, "UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + if ev.GetOrder() != nil { + pnl, err := bt.Portfolio.TrackFuturesOrder(ev, funds) + if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { + log.Errorf(log.BackTester, "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + err = bt.Statistic.AddPNLForTime(pnl) + if err != nil { + log.Errorf(log.BackTester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } } err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(log.BackTester, "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } - pnl, err := bt.Portfolio.GetLatestPNLForEvent(ev) - if err != nil { - log.Errorf(log.BackTester, "GetLatestPNLForEvent %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return - } - err = bt.Statistic.AddPNLForTime(pnl) - if err != nil { - log.Errorf(log.BackTester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - } } } diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 5b47e4a9eff..d0a3cd3fdf7 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -10,7 +10,7 @@ "exchange-name": "ftx", "asset": "spot", "currency": "USD", - "initial-funds": "100000", + "initial-funds": "10000", "transfer-fee": "0" } ], diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 3ddba0464ab..0bbb36c5d51 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -44,7 +44,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ClosePrice: data.Latest().GetClosePrice(), FillDependentEvent: o.GetFillDependentEvent(), } - if o.GetAssetType().IsFutures() { + if o.GetAssetType().IsFutures() && o.GetDirection() != common.ClosePosition { f.Amount = o.GetAllocatedFunds() } eventFunds := o.GetAllocatedFunds() @@ -75,8 +75,17 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * adjustedPrice, amount = slippage.CalculateSlippageByOrderbook(ob, o.GetDirection(), eventFunds, f.ExchangeFee) f.Slippage = adjustedPrice.Sub(f.ClosePrice).Div(f.ClosePrice).Mul(decimal.NewFromInt(100)) } else { - adjustedPrice, amount, err = e.sizeOfflineOrder(high, low, volume, &cs, f) - if err != nil { + slippageRate := slippage.EstimateSlippagePercentage(cs.MinimumSlippageRate, cs.MaximumSlippageRate) + if cs.SkipCandleVolumeFitting || o.IsClosingPosition() { + f.VolumeAdjustedPrice = f.ClosePrice + amount = f.Amount + } else { + f.VolumeAdjustedPrice, amount = ensureOrderFitsWithinHLV(f.ClosePrice, f.Amount, high, low, volume) + if !amount.Equal(f.GetAmount()) { + f.AppendReason(fmt.Sprintf("Order size shrunk from %v to %v to fit candle", f.Amount, amount)) + } + } + if amount.LessThanOrEqual(decimal.Zero) && f.GetAmount().GreaterThan(decimal.Zero) { switch f.GetDirection() { case gctorder.Buy: f.SetDirection(common.CouldNotBuy) @@ -89,9 +98,12 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * default: f.SetDirection(common.DoNothing) } - f.AppendReason(err.Error()) + f.AppendReason(fmt.Sprintf("amount set to 0, %s", errDataMayBeIncorrect)) return f, err } + adjustedPrice = applySlippageToPrice(f.GetDirection(), f.GetVolumeAdjustedPrice(), slippageRate) + f.Slippage = slippageRate.Mul(decimal.NewFromInt(100)).Sub(decimal.NewFromInt(100)) + f.ExchangeFee = calculateExchangeFee(adjustedPrice, amount, cs.TakerFee) } portfolioLimitedAmount := reduceAmountToFitPortfolioLimit(adjustedPrice, amount, eventFunds, f.GetDirection()) @@ -181,7 +193,11 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ords[i].CloseTime = o.GetTime() f.Order = &ords[i] f.PurchasePrice = decimal.NewFromFloat(ords[i].Price) - f.Total = f.PurchasePrice.Mul(limitReducedAmount).Add(f.ExchangeFee) + if ords[i].AssetType.IsFutures() { + f.Total = limitReducedAmount.Add(f.ExchangeFee) + } else { + f.Total = f.PurchasePrice.Mul(limitReducedAmount).Add(f.ExchangeFee) + } } if f.Order == nil { @@ -192,7 +208,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } // verifyOrderWithinLimits conforms the amount to fall into the minimum size and maximum size limit after reduced -func verifyOrderWithinLimits(f *fill.Fill, limitReducedAmount decimal.Decimal, cs *Settings) error { +func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, cs *Settings) error { if f == nil { return common.ErrNilEvent } @@ -259,7 +275,7 @@ func reduceAmountToFitPortfolioLimit(adjustedPrice, amount, sizedPortfolioTotal return amount } -func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal, useRealOrders, useExchangeLimits bool, f *fill.Fill, orderManager *engine.OrderManager) (string, error) { +func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal, useRealOrders, useExchangeLimits bool, f fill.Event, orderManager *engine.OrderManager) (string, error) { if f == nil { return "", common.ErrNilEvent } @@ -270,15 +286,15 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal var orderID string p, _ := price.Float64() a, _ := amount.Float64() - fee, _ := f.ExchangeFee.Float64() + fee, _ := f.GetExchangeFee().Float64() o := &gctorder.Submit{ Price: p, Amount: a, Fee: fee, - Exchange: f.Exchange, + Exchange: f.GetExchange(), ID: u.String(), - Side: f.Direction, - AssetType: f.AssetType, + Side: f.GetDirection(), + AssetType: f.GetAssetType(), Date: f.GetTime(), LastUpdated: f.GetTime(), Pair: f.Pair(), @@ -294,7 +310,7 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal return orderID, err } } else { - rate, _ := f.Amount.Float64() + rate, _ := f.GetAmount().Float64() submitResponse := gctorder.SubmitResponse{ IsOrderPlaced: true, OrderID: u.String(), @@ -314,32 +330,6 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal return orderID, nil } -func (e *Exchange) sizeOfflineOrder(high, low, volume decimal.Decimal, cs *Settings, f *fill.Fill) (adjustedPrice, adjustedAmount decimal.Decimal, err error) { - if cs == nil || f == nil { - return decimal.Zero, decimal.Zero, common.ErrNilArguments - } - // provide history and estimate volatility - slippageRate := slippage.EstimateSlippagePercentage(cs.MinimumSlippageRate, cs.MaximumSlippageRate) - if cs.SkipCandleVolumeFitting { - f.VolumeAdjustedPrice = f.ClosePrice - adjustedAmount = f.Amount - } else { - f.VolumeAdjustedPrice, adjustedAmount = ensureOrderFitsWithinHLV(f.ClosePrice, f.Amount, high, low, volume) - if !adjustedAmount.Equal(f.Amount) { - f.AppendReason(fmt.Sprintf("Order size shrunk from %v to %v to fit candle", f.Amount, adjustedAmount)) - } - } - - if adjustedAmount.LessThanOrEqual(decimal.Zero) && f.Amount.GreaterThan(decimal.Zero) { - return decimal.Zero, decimal.Zero, fmt.Errorf("amount set to 0, %w", errDataMayBeIncorrect) - } - adjustedPrice = applySlippageToPrice(f.GetDirection(), f.GetVolumeAdjustedPrice(), slippageRate) - - f.Slippage = slippageRate.Mul(decimal.NewFromInt(100)).Sub(decimal.NewFromInt(100)) - f.ExchangeFee = calculateExchangeFee(adjustedPrice, adjustedAmount, cs.TakerFee) - return adjustedPrice, adjustedAmount, nil -} - func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.Decimal) decimal.Decimal { adjustedPrice := price if direction == gctorder.Buy { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 752e28f2b4f..b74de438a52 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -82,6 +82,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi o.BuyLimit = ev.GetBuyLimit() o.SellLimit = ev.GetSellLimit() var sizingFunds decimal.Decimal + var side = ev.GetDirection() if ev.GetAssetType() == asset.Spot { pReader, err := funds.GetPairReader() if err != nil { @@ -93,18 +94,38 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi sizingFunds = pReader.QuoteAvailable() } } else if ev.GetAssetType().IsFutures() { - collateralFunds, err := funds.GetCollateralReader() - if err != nil { - return nil, err - } + if ev.GetDirection() == common.ClosePosition { + // lookup position + positions := lookup.FuturesTracker.GetPositions() + if len(positions) == 0 { + // cannot close a non existent position + return nil, errNoHoldings + } + sizingFunds = positions[len(positions)-1].Exposure + d := positions[len(positions)-1].OpeningDirection + switch d { + case gctorder.Short: + side = gctorder.Long + case gctorder.Long: + side = gctorder.Short + } + } else { + collateralFunds, err := funds.GetCollateralReader() + if err != nil { + return nil, err + } - sizingFunds = collateralFunds.AvailableFunds() + sizingFunds = collateralFunds.AvailableFunds() + } } if sizingFunds.LessThanOrEqual(decimal.Zero) { return cannotPurchase(ev, o, dir) } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) - + sizedOrder.SetDirection(side) + if ev.GetDirection() == common.ClosePosition { + sizedOrder.ClosingPosition = true + } return p.evaluateOrder(ev, o, sizedOrder) } @@ -484,40 +505,45 @@ func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) // TrackFuturesOrder updates the futures tracker with a new order // from a fill event -func (p *Portfolio) TrackFuturesOrder(detail *gctorder.Detail, fund funding.IFundReleaser) error { +func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) (*PNLSummary, error) { + detail := f.GetOrder() if detail == nil { - return gctorder.ErrSubmissionIsNil + return nil, gctorder.ErrSubmissionIsNil } if !detail.AssetType.IsFutures() { - return fmt.Errorf("order '%v' %w", detail.ID, gctorder.ErrNotFuturesAsset) + return nil, fmt.Errorf("order '%v' %w", detail.ID, gctorder.ErrNotFuturesAsset) } + collateralReleaser, err := fund.GetCollateralReleaser() if err != nil { - return fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) + return nil, fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) } settings, err := p.getSettings(detail.Exchange, detail.AssetType, detail.Pair) if err != nil { - return fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) + return nil, fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) } err = settings.FuturesTracker.TrackNewOrder(detail) if err != nil { - return err + return nil, err } pos := settings.FuturesTracker.GetPositions() if len(pos) == 0 { - return fmt.Errorf("%w should not happen", errNoHoldings) + return nil, fmt.Errorf("%w should not happen", errNoHoldings) } - if pos[len(pos)-1].Status == gctorder.Closed { + if pos[len(pos)-1].OpeningDirection != detail.Side { amount := decimal.NewFromFloat(detail.Amount) - err = collateralReleaser.TakeProfit(amount, pos[len(pos)-1].EntryAmount, pos[len(pos)-1].RealisedPNL) + value := decimal.NewFromFloat(detail.Price).Mul(amount) + err = collateralReleaser.TakeProfit(amount, value, pos[len(pos)-1].RealisedPNL) if err != nil { - return err + return nil, err } } - return nil + err = p.UpdatePNL(f, f.GetClosePrice()) + + return p.GetLatestPNLForEvent(f) } // GetLatestPNLForEvent takes in an event and returns the latest PNL data diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 7e3b5ecbb1b..236b87ddd6e 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -56,7 +56,7 @@ type Handler interface { GetComplianceManager(string, asset.Item, currency.Pair) (*compliance.Manager, error) GetFee(string, asset.Item, currency.Pair) decimal.Decimal GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) - TrackFuturesOrder(*gctorder.Detail, funding.IFundReleaser) error + TrackFuturesOrder(fill.Event, funding.IFundReleaser) (*PNLSummary, error) UpdatePNL(common.EventHandler, decimal.Decimal) error GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 002d485232b..b4aa203d1cb 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -25,6 +25,8 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc var amount decimal.Decimal var err error switch retOrder.GetDirection() { + case common.ClosePosition: + amount = amountAvailable case gctorder.Buy, gctorder.Long: // check size against currency specific settings amount, err = s.calculateBuySize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 09d4a4446ea..ea2b6e09719 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -132,6 +132,9 @@ func (s *Statistic) AddHoldingsForTime(h *holdings.Holding) error { } func (s *Statistic) AddPNLForTime(pnl *portfolio.PNLSummary) error { + if pnl == nil { + return fmt.Errorf("%w requires PNL", common.ErrNilArguments) + } if s.ExchangeAssetPairStatistics == nil { return errExchangeAssetPairStatsUnset } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index b9a30b435ac..c3ac4c2534b 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -95,13 +95,11 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf case len(pos) > 0 && v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position on last event") - futuresSignal.SetDirection(order.Long) response = append(response, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Open: if fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage) { futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position after reaching close short distance percentage") - futuresSignal.SetDirection(order.Long) response = append(response, &futuresSignal) } case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed: diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index 494a011a7f7..2ab4b380f95 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -88,3 +88,7 @@ func (o *Order) GetAllocatedFunds() decimal.Decimal { func (o *Order) GetFillDependentEvent() signal.Event { return o.FillDependentEvent } + +func (o *Order) IsClosingPosition() bool { + return o.ClosingPosition +} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index ddf05e11d8a..c843a64e8b8 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -22,6 +22,7 @@ type Order struct { BuyLimit decimal.Decimal SellLimit decimal.Decimal FillDependentEvent signal.Event + ClosingPosition bool } // Event inherits common event interfaces along with extra functions related to handling orders @@ -39,4 +40,5 @@ type Event interface { IsLeveraged() bool GetAllocatedFunds() decimal.Decimal GetFillDependentEvent() signal.Event + IsClosingPosition() bool } From 96936d0c2b9f600557689abaf05eeb089ae67b57 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 22 Feb 2022 17:13:29 +1100 Subject: [PATCH 081/171] vain attempts at tracking zeroing out bugs --- .../portfolio/holdings/holdings_test.go | 6 ++-- .../eventhandlers/portfolio/portfolio.go | 4 +-- .../portfolio/portfolio_types.go | 2 +- .../statistics/currencystatistics.go | 8 ++--- backtester/funding/funding_test.go | 4 +-- common/math/math_test.go | 4 +-- exchanges/order/futures.go | 29 ++++++++++++++----- 7 files changed, 36 insertions(+), 21 deletions(-) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index 7cec732c5f6..87dbe591823 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -163,7 +163,7 @@ func TestUpdateBuyStats(t *testing.T) { if !h.BoughtValue.Equal(decimal.NewFromInt(500)) { t.Errorf("expected '%v' received '%v'", 500, h.BoughtValue) } - if !h.SoldAmount.Equal(decimal.Zero) { + if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) } if !h.TotalFees.Equal(decimal.NewFromInt(1)) { @@ -212,7 +212,7 @@ func TestUpdateBuyStats(t *testing.T) { if !h.BoughtValue.Equal(decimal.NewFromInt(750)) { t.Errorf("expected '%v' received '%v'", 750, h.BoughtValue) } - if !h.SoldAmount.Equal(decimal.Zero) { + if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) } if !h.TotalFees.Equal(decimal.NewFromFloat(1.5)) { @@ -294,7 +294,7 @@ func TestUpdateSellStats(t *testing.T) { if !h.BoughtValue.Equal(decimal.NewFromInt(500)) { t.Errorf("expected '%v' received '%v'", 500, h.BoughtValue) } - if !h.SoldAmount.Equal(decimal.Zero) { + if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) } if !h.TotalFees.Equal(decimal.NewFromInt(1)) { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b74de438a52..05d83b053c6 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -575,7 +575,7 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er if len(pnlHistory) == 0 { return response, nil } - response.PNL = pnlHistory[len(pnlHistory)-1] + response.Result = pnlHistory[len(pnlHistory)-1] return response, nil } @@ -620,7 +620,7 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { if len(positions) > 0 { pnlHistory := positions[len(positions)-1].PNLHistory if len(pnlHistory) > 0 { - summary.PNL = pnlHistory[len(pnlHistory)-1] + summary.Result = pnlHistory[len(pnlHistory)-1] } } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 236b87ddd6e..2f28a5f2f08 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -88,5 +88,5 @@ type PNLSummary struct { Item asset.Item Pair currency.Pair Offset int64 - PNL gctorder.PNLResult + Result gctorder.PNLResult } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 9f9e2767c64..e7e3f0810ea 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -117,9 +117,9 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e c.TotalValueLostToSlippage = last.Holdings.TotalValueLostToSlippage.Round(2) c.TotalAssetValue = last.Holdings.BaseValue.Round(8) if last.PNL != nil { - c.UnrealisedPNL = last.PNL.PNL.UnrealisedPNL + c.UnrealisedPNL = last.PNL.Result.UnrealisedPNL // ???? - c.RealisedPNL = last.PNL.PNL.RealisedPNLBeforeFees + c.RealisedPNL = last.PNL.Result.RealisedPNLBeforeFees } if len(errs) > 0 { return errs @@ -209,8 +209,8 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. } if last.PNL != nil { - log.Infof(log.BackTester, "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.PNL.UnrealisedPNL, 8, ".", ",")) - log.Infof(log.BackTester, "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.PNL.RealisedPNLBeforeFees, 8, ".", ",")) + log.Infof(log.BackTester, "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) + log.Infof(log.BackTester, "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNLBeforeFees, 8, ".", ",")) } if len(errs) > 0 { log.Info(log.BackTester, "------------------Errors-------------------------------------") diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 813076a2b8d..79a60bba8ed 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -114,7 +114,7 @@ func TestTransfer(t *testing.T) { if !item2.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", item2.available, elite) } - if !item1.available.Equal(decimal.Zero) { + if !item1.available.IsZero() { t.Errorf("received '%v' expected '%v'", item1.available, decimal.Zero) } @@ -578,7 +578,7 @@ func TestIncreaseAvailablePair(t *testing.T) { t.Errorf("received '%v' expected '%v'", elite, pairItems.Quote.available) } pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) - if !pairItems.Base.available.Equal(decimal.Zero) { + if !pairItems.Base.available.IsZero() { t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.Base.available) } diff --git a/common/math/math_test.go b/common/math/math_test.go index 3a2fc1f0ad8..f4c6e788124 100644 --- a/common/math/math_test.go +++ b/common/math/math_test.go @@ -772,7 +772,7 @@ func TestDecimalGeometricAverage(t *testing.T) { if !errors.Is(err, errGeometricNegative) { t.Error(err) } - if !mean.Equal(decimal.Zero) { + if !mean.IsZero() { t.Errorf("expected %v, received %v", 0, mean) } } @@ -829,7 +829,7 @@ func TestDecimalFinancialGeometricAverage(t *testing.T) { if err != nil { t.Error(err) } - if !mean.Equal(decimal.Zero) { + if !mean.IsZero() { t.Errorf("expected %v, received %v", 0, mean) } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 8f99db234bf..63a2b3c4b34 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -12,6 +12,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -372,15 +373,24 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro }() price := decimal.NewFromFloat(currentPrice) result := &PNLResult{ - Time: t, - Price: price, + Time: t, + Price: price, + UnrealisedPNL: p.unrealisedPNL, + RealisedPNLBeforeFees: p.realisedPNL, + Exposure: p.exposure, } + var diff decimal.Decimal if p.currentDirection.IsLong() { - diff := price.Sub(p.entryPrice) - result.UnrealisedPNL = p.exposure.Mul(diff) + diff = price.Sub(p.entryPrice) } else if p.currentDirection.IsShort() { - diff := p.entryPrice.Sub(price) + diff = p.entryPrice.Sub(price) + + } + var updatedUPNL = false + if !diff.IsZero() { + // only update if different result.UnrealisedPNL = p.exposure.Mul(diff) + updatedUPNL = true } if len(p.pnlHistory) > 0 { result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees @@ -388,7 +398,9 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro } var err error p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) - p.unrealisedPNL = result.UnrealisedPNL + if updatedUPNL { + p.unrealisedPNL = result.UnrealisedPNL + } return err } @@ -561,6 +573,9 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { result.RealisedPNLBeforeFees = decimal.Zero p.status = Closed } + if result.RealisedPNLBeforeFees.IsZero() && result.UnrealisedPNL.IsZero() { + log.Debug(log.BackTester, "woah nelly") + } p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) if err != nil { return err @@ -582,7 +597,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.exposure = shortSide.Sub(longSide) } - if p.exposure.Equal(decimal.Zero) { + if p.exposure.IsZero() { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) p.realisedPNL = CalculateRealisedPNL(p.pnlHistory) From e71be130eb9b02fe34e4d31ade4c28eb141feb53 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 24 Feb 2022 11:07:33 +1100 Subject: [PATCH 082/171] woww, super fun new subloggers :tada: --- README.md | 10 +- backtester/common/common_types.go | 47 +++++ backtester/config/config.go | 131 ++++++------ backtester/data/kline/csv/csv.go | 2 +- backtester/data/kline/database/database.go | 2 +- backtester/data/kline/kline.go | 12 +- backtester/{backtest => engine}/README.md | 10 +- backtester/{backtest => engine}/backtest.go | 189 +++--------------- .../{backtest => engine}/backtest_test.go | 0 .../{backtest => engine}/backtest_types.go | 0 backtester/engine/live.go | 139 +++++++++++++ backtester/{backtest => engine}/setup.go | 30 +-- .../eventhandlers/portfolio/portfolio.go | 4 +- .../statistics/currencystatistics.go | 108 +++++----- .../statistics/fundingstatistics.go | 82 ++++---- .../eventhandlers/statistics/statistics.go | 58 +++--- backtester/main.go | 22 +- backtester/report/report.go | 6 +- ...dme.tmpl => backtester_engine_readme.tmpl} | 4 +- exchanges/order/futures.go | 4 - log/logger.go | 5 +- log/logger_setup.go | 1 - log/logger_test.go | 4 +- log/sublogger.go | 2 +- log/sublogger_types.go | 1 - 25 files changed, 474 insertions(+), 399 deletions(-) rename backtester/{backtest => engine}/README.md (90%) rename backtester/{backtest => engine}/backtest.go (58%) rename backtester/{backtest => engine}/backtest_test.go (100%) rename backtester/{backtest => engine}/backtest_types.go (100%) create mode 100644 backtester/engine/live.go rename backtester/{backtest => engine}/setup.go (94%) rename cmd/documentation/backtester_templates/{backtester_backtest_readme.tmpl => backtester_engine_readme.tmpl} (77%) diff --git a/README.md b/README.md index 8cebce64bf5..784fd198d41 100644 --- a/README.md +++ b/README.md @@ -143,13 +143,13 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| -| [thrasher-](https://github.com/thrasher-) | 662 | -| [shazbert](https://github.com/shazbert) | 231 | +| [thrasher-](https://github.com/thrasher-) | 664 | +| [shazbert](https://github.com/shazbert) | 234 | | [gloriousCode](https://github.com/gloriousCode) | 194 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | -| [dependabot[bot]](https://github.com/apps/dependabot) | 50 | +| [dependabot[bot]](https://github.com/apps/dependabot) | 57 | | [xtda](https://github.com/xtda) | 47 | -| [lrascao](https://github.com/lrascao) | 21 | +| [lrascao](https://github.com/lrascao) | 27 | | [Rots](https://github.com/Rots) | 15 | | [vazha](https://github.com/vazha) | 15 | | [ydm](https://github.com/ydm) | 15 | @@ -160,7 +160,7 @@ Binaries will be published once the codebase reaches a stable condition. | [marcofranssen](https://github.com/marcofranssen) | 8 | | [dackroyd](https://github.com/dackroyd) | 5 | | [cranktakular](https://github.com/cranktakular) | 5 | -| [khcchiu](https://github.com/khcchiu) | 4 | +| [khcchiu](https://github.com/khcchiu) | 5 | | [woshidama323](https://github.com/woshidama323) | 3 | | [yangrq1018](https://github.com/yangrq1018) | 3 | | [TaltaM](https://github.com/TaltaM) | 3 | diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 8a1bddc2346..96d6a19e75b 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -9,6 +9,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" ) const ( @@ -73,6 +74,52 @@ type EventHandler interface { AppendReason(string) } +// List of all the custom sublogger names +const ( + Backtester = "Backtester" + Setup = "Setup" + Strategy = "Strategy" + Config = "Config" + Portfolio = "Portfolio" + Exchange = "Exchange" + Fill = "Fill" + Funding = "Funding" + Report = "Report" + Statistics = "Statistics" + CurrencyStatistics = "CurrencyStatistics" + FundingStatistics = "FundingStatistics" + Compliance = "Compliance" + Sizing = "Sizing" + Risk = "Risk" + Holdings = "Holdings" + Slippage = "Slippage" + Data = "Data" +) + +var ( + // SubLoggers is a map of loggers to use across the backtester + SubLoggers = map[string]*log.SubLogger{ + Backtester: nil, + Setup: nil, + Strategy: nil, + Config: nil, + Portfolio: nil, + Exchange: nil, + Fill: nil, + Funding: nil, + Report: nil, + Statistics: nil, + CurrencyStatistics: nil, + FundingStatistics: nil, + Compliance: nil, + Sizing: nil, + Risk: nil, + Holdings: nil, + Slippage: nil, + Data: nil, + } +) + // DataEventHandler interface used for loading and interacting with Data type DataEventHandler interface { EventHandler diff --git a/backtester/config/config.go b/backtester/config/config.go index 2b5c40a50d9..53603a12b6b 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" gctcommon "github.com/thrasher-corp/gocryptotrader/common" @@ -38,28 +39,28 @@ func LoadConfig(data []byte) (resp *Config, err error) { // PrintSetting prints relevant settings to the console for easy reading func (c *Config) PrintSetting() { - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------Backtester Settings------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------Strategy Settings--------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Strategy: %s", c.StrategySettings.Name) + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------Backtester Settings------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------Strategy Settings--------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Strategy: %s", c.StrategySettings.Name) if len(c.StrategySettings.CustomSettings) > 0 { - log.Info(log.BackTester, "Custom strategy variables:") + log.Info(common.SubLoggers[common.Config], "Custom strategy variables:") for k, v := range c.StrategySettings.CustomSettings { - log.Infof(log.BackTester, "%s: %v", k, v) + log.Infof(common.SubLoggers[common.Config], "%s: %v", k, v) } } else { - log.Info(log.BackTester, "Custom strategy variables: unset") + log.Info(common.SubLoggers[common.Config], "Custom strategy variables: unset") } - log.Infof(log.BackTester, "Simultaneous Signal Processing: %v", c.StrategySettings.SimultaneousSignalProcessing) - log.Infof(log.BackTester, "Use Exchange Level Funding: %v", c.StrategySettings.UseExchangeLevelFunding) - log.Infof(log.BackTester, "USD value tracking: %v", !c.StrategySettings.DisableUSDTracking) + log.Infof(common.SubLoggers[common.Config], "Simultaneous Signal Processing: %v", c.StrategySettings.SimultaneousSignalProcessing) + log.Infof(common.SubLoggers[common.Config], "Use Exchange Level Funding: %v", c.StrategySettings.UseExchangeLevelFunding) + log.Infof(common.SubLoggers[common.Config], "USD value tracking: %v", !c.StrategySettings.DisableUSDTracking) if c.StrategySettings.UseExchangeLevelFunding && c.StrategySettings.SimultaneousSignalProcessing { - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------Funding Settings---------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------Funding Settings---------------------------") for i := range c.StrategySettings.ExchangeLevelFunding { - log.Infof(log.BackTester, "Initial funds for %v %v %v: %v", + log.Infof(common.SubLoggers[common.Config], "Initial funds for %v %v %v: %v", c.StrategySettings.ExchangeLevelFunding[i].ExchangeName, c.StrategySettings.ExchangeLevelFunding[i].Asset, c.StrategySettings.ExchangeLevelFunding[i].Currency, @@ -68,80 +69,80 @@ func (c *Config) PrintSetting() { } for i := range c.CurrencySettings { - log.Info(log.BackTester, "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") currStr := fmt.Sprintf("------------------%v %v-%v Currency Settings---------------------------------------------------------", c.CurrencySettings[i].Asset, c.CurrencySettings[i].Base, c.CurrencySettings[i].Quote) - log.Infof(log.BackTester, currStr[:61]) - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Exchange: %v", c.CurrencySettings[i].ExchangeName) + log.Infof(common.SubLoggers[common.Config], currStr[:61]) + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Exchange: %v", c.CurrencySettings[i].ExchangeName) if !c.StrategySettings.UseExchangeLevelFunding && c.CurrencySettings[i].SpotDetails != nil { if c.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { - log.Infof(log.BackTester, "Initial base funds: %v %v", + log.Infof(common.SubLoggers[common.Config], "Initial base funds: %v %v", c.CurrencySettings[i].SpotDetails.InitialBaseFunds.Round(8), c.CurrencySettings[i].Base) } if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil { - log.Infof(log.BackTester, "Initial quote funds: %v %v", + log.Infof(common.SubLoggers[common.Config], "Initial quote funds: %v %v", c.CurrencySettings[i].SpotDetails.InitialQuoteFunds.Round(8), c.CurrencySettings[i].Quote) } } - log.Infof(log.BackTester, "Maker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) - log.Infof(log.BackTester, "Taker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) - log.Infof(log.BackTester, "Minimum slippage percent %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) - log.Infof(log.BackTester, "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) - log.Infof(log.BackTester, "Buy rules: %+v", c.CurrencySettings[i].BuySide) - log.Infof(log.BackTester, "Sell rules: %+v", c.CurrencySettings[i].SellSide) + log.Infof(common.SubLoggers[common.Config], "Maker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) + log.Infof(common.SubLoggers[common.Config], "Taker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) + log.Infof(common.SubLoggers[common.Config], "Minimum slippage percent %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) + log.Infof(common.SubLoggers[common.Config], "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) + log.Infof(common.SubLoggers[common.Config], "Buy rules: %+v", c.CurrencySettings[i].BuySide) + log.Infof(common.SubLoggers[common.Config], "Sell rules: %+v", c.CurrencySettings[i].SellSide) if c.CurrencySettings[i].FuturesDetails != nil && c.CurrencySettings[i].Asset == asset.Futures.String() { - log.Infof(log.BackTester, "Leverage rules: %+v", c.CurrencySettings[i].FuturesDetails.Leverage) + log.Infof(common.SubLoggers[common.Config], "Leverage rules: %+v", c.CurrencySettings[i].FuturesDetails.Leverage) } - log.Infof(log.BackTester, "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) + log.Infof(common.SubLoggers[common.Config], "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) } - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------Portfolio Settings-------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Buy rules: %+v", c.PortfolioSettings.BuySide) - log.Infof(log.BackTester, "Sell rules: %+v", c.PortfolioSettings.SellSide) - log.Infof(log.BackTester, "Leverage rules: %+v", c.PortfolioSettings.Leverage) + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------Portfolio Settings-------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Buy rules: %+v", c.PortfolioSettings.BuySide) + log.Infof(common.SubLoggers[common.Config], "Sell rules: %+v", c.PortfolioSettings.SellSide) + log.Infof(common.SubLoggers[common.Config], "Leverage rules: %+v", c.PortfolioSettings.Leverage) if c.DataSettings.LiveData != nil { - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------Live Settings------------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Data type: %v", c.DataSettings.DataType) - log.Infof(log.BackTester, "Interval: %v", c.DataSettings.Interval) - log.Infof(log.BackTester, "REAL ORDERS: %v", c.DataSettings.LiveData.RealOrders) - log.Infof(log.BackTester, "Overriding GCT API settings: %v", c.DataSettings.LiveData.APIClientIDOverride != "") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------Live Settings------------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) + log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) + log.Infof(common.SubLoggers[common.Config], "REAL ORDERS: %v", c.DataSettings.LiveData.RealOrders) + log.Infof(common.SubLoggers[common.Config], "Overriding GCT API settings: %v", c.DataSettings.LiveData.APIClientIDOverride != "") } if c.DataSettings.APIData != nil { - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------API Settings-------------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Data type: %v", c.DataSettings.DataType) - log.Infof(log.BackTester, "Interval: %v", c.DataSettings.Interval) - log.Infof(log.BackTester, "Start date: %v", c.DataSettings.APIData.StartDate.Format(gctcommon.SimpleTimeFormat)) - log.Infof(log.BackTester, "End date: %v", c.DataSettings.APIData.EndDate.Format(gctcommon.SimpleTimeFormat)) + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------API Settings-------------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) + log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) + log.Infof(common.SubLoggers[common.Config], "Start date: %v", c.DataSettings.APIData.StartDate.Format(gctcommon.SimpleTimeFormat)) + log.Infof(common.SubLoggers[common.Config], "End date: %v", c.DataSettings.APIData.EndDate.Format(gctcommon.SimpleTimeFormat)) } if c.DataSettings.CSVData != nil { - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------CSV Settings-------------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Data type: %v", c.DataSettings.DataType) - log.Infof(log.BackTester, "Interval: %v", c.DataSettings.Interval) - log.Infof(log.BackTester, "CSV file: %v", c.DataSettings.CSVData.FullPath) + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------CSV Settings-------------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) + log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) + log.Infof(common.SubLoggers[common.Config], "CSV file: %v", c.DataSettings.CSVData.FullPath) } if c.DataSettings.DatabaseData != nil { - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Info(log.BackTester, "------------------Database Settings--------------------------") - log.Info(log.BackTester, "-------------------------------------------------------------") - log.Infof(log.BackTester, "Data type: %v", c.DataSettings.DataType) - log.Infof(log.BackTester, "Interval: %v", c.DataSettings.Interval) - log.Infof(log.BackTester, "Start date: %v", c.DataSettings.DatabaseData.StartDate.Format(gctcommon.SimpleTimeFormat)) - log.Infof(log.BackTester, "End date: %v", c.DataSettings.DatabaseData.EndDate.Format(gctcommon.SimpleTimeFormat)) + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], "------------------Database Settings--------------------------") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) + log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) + log.Infof(common.SubLoggers[common.Config], "Start date: %v", c.DataSettings.DatabaseData.StartDate.Format(gctcommon.SimpleTimeFormat)) + log.Infof(common.SubLoggers[common.Config], "End date: %v", c.DataSettings.DatabaseData.EndDate.Format(gctcommon.SimpleTimeFormat)) } - log.Info(log.BackTester, "-------------------------------------------------------------\n\n") + log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------\n\n") } // Validate checks all config settings @@ -287,8 +288,8 @@ func (c *Config) validateCurrencySettings() error { if c.CurrencySettings[i].SpotDetails != nil { // if c.CurrencySettings[i].SpotDetails.InitialLegacyFunds > 0 { // // temporarily migrate legacy start config value - // log.Warn(log.BackTester, "config field 'initial-funds' no longer supported, please use 'initial-quote-funds'") - // log.Warnf(log.BackTester, "temporarily setting 'initial-quote-funds' to 'initial-funds' value of %v", c.CurrencySettings[i].InitialLegacyFunds) + // log.Warn(common.SubLoggers[common.Config], "config field 'initial-funds' no longer supported, please use 'initial-quote-funds'") + // log.Warnf(common.SubLoggers[common.Config], "temporarily setting 'initial-quote-funds' to 'initial-funds' value of %v", c.CurrencySettings[i].InitialLegacyFunds) // iqf := decimal.NewFromFloat(c.CurrencySettings[i].SpotDetails.InitialLegacyFunds) // c.CurrencySettings[i].SpotDetails.InitialQuoteFunds = &iqf // } diff --git a/backtester/data/kline/csv/csv.go b/backtester/data/kline/csv/csv.go index 39c2d6a75e0..446570bbaa8 100644 --- a/backtester/data/kline/csv/csv.go +++ b/backtester/data/kline/csv/csv.go @@ -33,7 +33,7 @@ func LoadData(dataType int64, filepath, exchangeName string, interval time.Durat defer func() { err = csvFile.Close() if err != nil { - log.Errorln(log.BackTester, err) + log.Errorln(common.SubLoggers[common.Data], err) } }() diff --git a/backtester/data/kline/database/database.go b/backtester/data/kline/database/database.go index f9c4571c082..af6e50253ab 100644 --- a/backtester/data/kline/database/database.go +++ b/backtester/data/kline/database/database.go @@ -38,7 +38,7 @@ func LoadData(startDate, endDate time.Time, interval time.Duration, exchangeName resp.Item = klineItem for i := range klineItem.Candles { if klineItem.Candles[i].ValidationIssues != "" { - log.Warnf(log.BackTester, "candle validation issue for %v %v %v: %v", klineItem.Exchange, klineItem.Asset, klineItem.Pair, klineItem.Candles[i].ValidationIssues) + log.Warnf(common.SubLoggers[common.Data], "candle validation issue for %v %v %v: %v", klineItem.Exchange, klineItem.Asset, klineItem.Pair, klineItem.Candles[i].ValidationIssues) } } case common.DataTrade: diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index 1a76f39f07b..0a3a410c872 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -94,7 +94,7 @@ func (d *DataFromKline) AppendResults(ki *gctkline.Item) { d.RangeHolder.Ranges[i].Intervals[j].HasData = true } } - log.Debugf(log.BackTester, "appending %v candle intervals: %v", len(gctCandles), candleTimes) + log.Debugf(common.SubLoggers[common.Data], "appending %v candle intervals: %v", len(gctCandles), candleTimes) d.AppendStream(klineData...) d.SortStream() } @@ -109,7 +109,7 @@ func (d *DataFromKline) StreamOpen() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Open } else { - log.Errorf(log.BackTester, "incorrect data loaded into stream") + log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") } } return ret @@ -125,7 +125,7 @@ func (d *DataFromKline) StreamHigh() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.High } else { - log.Errorf(log.BackTester, "incorrect data loaded into stream") + log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") } } return ret @@ -141,7 +141,7 @@ func (d *DataFromKline) StreamLow() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Low } else { - log.Errorf(log.BackTester, "incorrect data loaded into stream") + log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") } } return ret @@ -157,7 +157,7 @@ func (d *DataFromKline) StreamClose() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Close } else { - log.Errorf(log.BackTester, "incorrect data loaded into stream") + log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") } } return ret @@ -173,7 +173,7 @@ func (d *DataFromKline) StreamVol() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Volume } else { - log.Errorf(log.BackTester, "incorrect data loaded into stream") + log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") } } return ret diff --git a/backtester/backtest/README.md b/backtester/engine/README.md similarity index 90% rename from backtester/backtest/README.md rename to backtester/engine/README.md index 754c403603a..682917a9b67 100644 --- a/backtester/backtest/README.md +++ b/backtester/engine/README.md @@ -1,16 +1,16 @@ -# GoCryptoTrader Backtester: Backtest package +# GoCryptoTrader Backtester: Engine package [![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) [![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) -[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/backtester/backtest) +[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/backtester/engine) [![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) -This backtest package is part of the GoCryptoTrader codebase. +This engine package is part of the GoCryptoTrader codebase. ## This is still in active development @@ -18,9 +18,9 @@ You can track ideas, planned features and what's in progress on this Trello boar Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) -## Backtest package overview +## Engine package overview -The backtest package is the most important package of the GoCryptoTrader backtester. It is the engine which combines all elements. +The engine package is the most important package of the GoCryptoTrader backtester. It is the engine which combines all elements. It is responsible for the following functionality - Loading settings from a provided config file - Retrieving data diff --git a/backtester/backtest/backtest.go b/backtester/engine/backtest.go similarity index 58% rename from backtester/backtest/backtest.go rename to backtester/engine/backtest.go index e20b2dff0db..0986313e0bb 100644 --- a/backtester/backtest/backtest.go +++ b/backtester/engine/backtest.go @@ -1,17 +1,13 @@ package backtest import ( - "context" "errors" "fmt" - "time" "github.com/thrasher-corp/gocryptotrader/backtester/common" - "github.com/thrasher-corp/gocryptotrader/backtester/config" "github.com/thrasher-corp/gocryptotrader/backtester/data" - "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" - "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/live" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" @@ -20,10 +16,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" - "github.com/thrasher-corp/gocryptotrader/currency" - gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -53,7 +45,7 @@ func (bt *BackTest) Reset() { // Run will iterate over loaded data events // save them and then handle the event based on its type func (bt *BackTest) Run() error { - log.Info(log.BackTester, "running backtester against pre-defined data") + log.Info(common.SubLoggers[common.Backtester], "running backtester against pre-defined data") dataLoadingIssue: for ev := bt.EventQueue.NextEvent(); ; ev = bt.EventQueue.NextEvent() { if ev == nil { @@ -65,7 +57,7 @@ dataLoadingIssue: d := dataHandler.Next() if d == nil { if !bt.hasHandledEvent { - log.Errorf(log.BackTester, "Unable to perform `Next` for %v %v %v", exchangeName, assetItem, currencyPair) + log.Errorf(common.SubLoggers[common.Backtester], "Unable to perform `Next` for %v %v %v", exchangeName, assetItem, currencyPair) } break dataLoadingIssue } @@ -156,12 +148,12 @@ func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds fun // too much bad data is a severe error and backtesting must cease return err } - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Backtester], err) return nil } err = bt.Statistic.SetEventForOffset(s) if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Backtester], err) } bt.EventQueue.AppendEvent(s) @@ -201,13 +193,13 @@ func (bt *BackTest) processSimultaneousDataEvents() error { // too much bad data is a severe error and backtesting must cease return err } - log.Errorf(log.BackTester, "OnSimultaneousSignals %v", err) + log.Errorf(common.SubLoggers[common.Backtester], "OnSimultaneousSignals %v", err) return nil } for i := range signals { err = bt.Statistic.SetEventForOffset(signals[i]) if err != nil { - log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", signals[i].GetExchange(), signals[i].GetAssetType(), signals[i].Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", signals[i].GetExchange(), signals[i].GetAssetType(), signals[i].Pair(), err) } bt.EventQueue.AppendEvent(signals[i]) } @@ -223,12 +215,12 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if err == statistics.ErrAlreadyProcessed { return err } - log.Errorf(log.BackTester, "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } // update portfolio manager with the latest price err = bt.Portfolio.UpdateHoldings(ev, funds) if err != nil { - log.Errorf(log.BackTester, "UpdateHoldings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "UpdateHoldings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } if ev.GetAssetType().IsFutures() { @@ -243,9 +235,16 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if errors.Is(err, gctorder.ErrPositionLiquidated) { cr.Liquidate() } else { - log.Errorf(log.BackTester, "UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return nil } } + var pnl *portfolio.PNLSummary + pnl, err = bt.Portfolio.GetLatestPNLForEvent(ev) + if err != nil { + return err + } + return bt.Statistic.AddPNLForTime(pnl) } return nil @@ -255,18 +254,18 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) { cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Errorf(log.BackTester, "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } var o *order.Order o, err = bt.Portfolio.OnSignal(ev, &cs, funds) if err != nil { - log.Errorf(log.BackTester, "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } err = bt.Statistic.SetEventForOffset(o) if err != nil { - log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(o) @@ -277,14 +276,14 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease f, err := bt.Exchange.ExecuteOrder(ev, d, bt.orderManager, funds) if err != nil { if f == nil { - log.Errorf(log.BackTester, "fill event should always be returned, please fix, %v", err) + log.Errorf(common.SubLoggers[common.Backtester], "fill event should always be returned, please fix, %v", err) return } - log.Errorf(log.BackTester, "%v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "%v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) } err = bt.Statistic.SetEventForOffset(f) if err != nil { - log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(f) } @@ -292,39 +291,39 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) { t, err := bt.Portfolio.OnFill(ev, funds) if err != nil { - log.Errorf(log.BackTester, "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } err = bt.Statistic.SetEventForOffset(t) if err != nil { - log.Errorf(log.BackTester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var holding *holdings.Holding holding, err = bt.Portfolio.ViewHoldingAtTimePeriod(ev) if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Backtester], err) } if holding == nil { - log.Error(log.BackTester, "why is holdings nill?") + log.Error(common.SubLoggers[common.Backtester], "why is holdings nill?") } else { err = bt.Statistic.AddHoldingsForTime(holding) if err != nil { - log.Errorf(log.BackTester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } var cp *compliance.Manager cp, err = bt.Portfolio.GetComplianceManager(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Errorf(log.BackTester, "GetComplianceManager %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "GetComplianceManager %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } snap := cp.GetLatestSnapshot() err = bt.Statistic.AddComplianceSnapshotForTime(snap, ev) if err != nil { - log.Errorf(log.BackTester, "AddComplianceSnapshotForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "AddComplianceSnapshotForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } fde := ev.GetFillDependentEvent() @@ -336,144 +335,22 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if ev.GetOrder() != nil { pnl, err := bt.Portfolio.TrackFuturesOrder(ev, funds) if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { - log.Errorf(log.BackTester, "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } err = bt.Statistic.AddPNLForTime(pnl) if err != nil { - log.Errorf(log.BackTester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Errorf(log.BackTester, "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return - } - } -} - -// RunLive is a proof of concept function that does not yet support multi currency usage -// It runs by constantly checking for new live datas and running through the list of events -// once new data is processed. It will run until application close event has been received -func (bt *BackTest) RunLive() error { - log.Info(log.BackTester, "running backtester against live data") - timeoutTimer := time.NewTimer(time.Minute * 5) - // a frequent timer so that when a new candle is released by an exchange - // that it can be processed quickly - processEventTicker := time.NewTicker(time.Second) - doneARun := false - for { - select { - case <-bt.shutdown: - return nil - case <-timeoutTimer.C: - return errLiveDataTimeout - case <-processEventTicker.C: - for e := bt.EventQueue.NextEvent(); ; e = bt.EventQueue.NextEvent() { - if e == nil { - // as live only supports singular currency, just get the proper reference manually - var d data.Handler - dd := bt.Datas.GetAllData() - for k1, v1 := range dd { - for k2, v2 := range v1 { - for k3 := range v2 { - d = dd[k1][k2][k3] - } - } - } - de := d.Next() - if de == nil { - break - } - - bt.EventQueue.AppendEvent(de) - doneARun = true - continue - } - err := bt.handleEvent(e) - if err != nil { - return err - } - } - if doneARun { - timeoutTimer = time.NewTimer(time.Minute * 5) - } - } - } -} - -// loadLiveDataLoop is an incomplete function to continuously retrieve exchange data on a loop -// from live. Its purpose is to be able to perform strategy analysis against current data -func (bt *BackTest) loadLiveDataLoop(resp *kline.DataFromKline, cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, dataType int64) { - startDate := time.Now().Add(-cfg.DataSettings.Interval * 2) - dates, err := gctkline.CalculateCandleDateRanges( - startDate, - startDate.AddDate(1, 0, 0), - gctkline.Interval(cfg.DataSettings.Interval), - 0) - if err != nil { - log.Errorf(log.BackTester, "%v. Please check your GoCryptoTrader configuration", err) - return - } - candles, err := live.LoadData(context.TODO(), - exch, - dataType, - cfg.DataSettings.Interval, - fPair, - a) - if err != nil { - log.Errorf(log.BackTester, "%v. Please check your GoCryptoTrader configuration", err) - return - } - dates.SetHasDataFromCandles(candles.Candles) - resp.RangeHolder = dates - resp.Item = *candles - - loadNewDataTimer := time.NewTimer(time.Second * 5) - for { - select { - case <-bt.shutdown: + log.Errorf(common.SubLoggers[common.Backtester], "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return - case <-loadNewDataTimer.C: - log.Infof(log.BackTester, "fetching data for %v %v %v %v", exch.GetName(), a, fPair, cfg.DataSettings.Interval) - loadNewDataTimer.Reset(time.Second * 15) - err = bt.loadLiveData(resp, cfg, exch, fPair, a, dataType) - if err != nil { - log.Error(log.BackTester, err) - return - } } } } -func (bt *BackTest) loadLiveData(resp *kline.DataFromKline, cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, dataType int64) error { - if resp == nil { - return errNilData - } - if cfg == nil { - return errNilConfig - } - if exch == nil { - return errNilExchange - } - candles, err := live.LoadData(context.TODO(), - exch, - dataType, - cfg.DataSettings.Interval, - fPair, - a) - if err != nil { - return err - } - if len(candles.Candles) == 0 { - return nil - } - resp.AppendResults(candles) - bt.Reports.UpdateItem(&resp.Item) - log.Info(log.BackTester, "sleeping for 30 seconds before checking for new candle data") - return nil -} - // Stop shuts down the live data loop func (bt *BackTest) Stop() { close(bt.shutdown) diff --git a/backtester/backtest/backtest_test.go b/backtester/engine/backtest_test.go similarity index 100% rename from backtester/backtest/backtest_test.go rename to backtester/engine/backtest_test.go diff --git a/backtester/backtest/backtest_types.go b/backtester/engine/backtest_types.go similarity index 100% rename from backtester/backtest/backtest_types.go rename to backtester/engine/backtest_types.go diff --git a/backtester/engine/live.go b/backtester/engine/live.go new file mode 100644 index 00000000000..62c79934492 --- /dev/null +++ b/backtester/engine/live.go @@ -0,0 +1,139 @@ +package backtest + +import ( + "context" + "time" + + "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/config" + "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/data/kline/live" + "github.com/thrasher-corp/gocryptotrader/currency" + gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/log" +) + +// RunLive is a proof of concept function that does not yet support multi currency usage +// It runs by constantly checking for new live datas and running through the list of events +// once new data is processed. It will run until application close event has been received +func (bt *BackTest) RunLive() error { + log.Info(common.SubLoggers[common.Backtester], "running backtester against live data") + timeoutTimer := time.NewTimer(time.Minute * 5) + // a frequent timer so that when a new candle is released by an exchange + // that it can be processed quickly + processEventTicker := time.NewTicker(time.Second) + doneARun := false + for { + select { + case <-bt.shutdown: + return nil + case <-timeoutTimer.C: + return errLiveDataTimeout + case <-processEventTicker.C: + for e := bt.EventQueue.NextEvent(); ; e = bt.EventQueue.NextEvent() { + if e == nil { + // as live only supports singular currency, just get the proper reference manually + var d data.Handler + dd := bt.Datas.GetAllData() + for k1, v1 := range dd { + for k2, v2 := range v1 { + for k3 := range v2 { + d = dd[k1][k2][k3] + } + } + } + de := d.Next() + if de == nil { + break + } + + bt.EventQueue.AppendEvent(de) + doneARun = true + continue + } + err := bt.handleEvent(e) + if err != nil { + return err + } + } + if doneARun { + timeoutTimer = time.NewTimer(time.Minute * 5) + } + } + } +} + +// loadLiveDataLoop is an incomplete function to continuously retrieve exchange data on a loop +// from live. Its purpose is to be able to perform strategy analysis against current data +func (bt *BackTest) loadLiveDataLoop(resp *kline.DataFromKline, cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, dataType int64) { + startDate := time.Now().Add(-cfg.DataSettings.Interval * 2) + dates, err := gctkline.CalculateCandleDateRanges( + startDate, + startDate.AddDate(1, 0, 0), + gctkline.Interval(cfg.DataSettings.Interval), + 0) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "%v. Please check your GoCryptoTrader configuration", err) + return + } + candles, err := live.LoadData(context.TODO(), + exch, + dataType, + cfg.DataSettings.Interval, + fPair, + a) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "%v. Please check your GoCryptoTrader configuration", err) + return + } + dates.SetHasDataFromCandles(candles.Candles) + resp.RangeHolder = dates + resp.Item = *candles + + loadNewDataTimer := time.NewTimer(time.Second * 5) + for { + select { + case <-bt.shutdown: + return + case <-loadNewDataTimer.C: + log.Infof(common.SubLoggers[common.Backtester], "fetching data for %v %v %v %v", exch.GetName(), a, fPair, cfg.DataSettings.Interval) + loadNewDataTimer.Reset(time.Second * 15) + err = bt.loadLiveData(resp, cfg, exch, fPair, a, dataType) + if err != nil { + log.Error(common.SubLoggers[common.Backtester], err) + return + } + } + } +} + +func (bt *BackTest) loadLiveData(resp *kline.DataFromKline, cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, dataType int64) error { + if resp == nil { + return errNilData + } + if cfg == nil { + return errNilConfig + } + if exch == nil { + return errNilExchange + } + candles, err := live.LoadData(context.TODO(), + exch, + dataType, + cfg.DataSettings.Interval, + fPair, + a) + if err != nil { + return err + } + if len(candles.Candles) == 0 { + return nil + } + resp.AppendResults(candles) + bt.Reports.UpdateItem(&resp.Item) + log.Info(common.SubLoggers[common.Backtester], "sleeping for 30 seconds before checking for new candle data") + return nil +} diff --git a/backtester/backtest/setup.go b/backtester/engine/setup.go similarity index 94% rename from backtester/backtest/setup.go rename to backtester/engine/setup.go index 2aef5b0bccc..6298f733068 100644 --- a/backtester/backtest/setup.go +++ b/backtester/engine/setup.go @@ -43,7 +43,7 @@ import ( // NewFromConfig takes a strategy config and configures a backtester variable to run func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool) (*BackTest, error) { - log.Infoln(log.BackTester, "loading config...") + log.Infoln(common.SubLoggers[common.Setup], "loading config...") if cfg == nil { return nil, errNilConfig } @@ -231,7 +231,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool } portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet if cfg.CurrencySettings[i].MakerFee.GreaterThan(cfg.CurrencySettings[i].TakerFee) { - log.Warnf(log.BackTester, "maker fee '%v' should not exceed taker fee '%v'. Please review config", + log.Warnf(common.SubLoggers[common.Setup], "maker fee '%v' should not exceed taker fee '%v'. Please review config", cfg.CurrencySettings[i].MakerFee, cfg.CurrencySettings[i].TakerFee) } @@ -422,7 +422,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool } func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange, error) { - log.Infoln(log.BackTester, "setting exchange settings...") + log.Infoln(common.SubLoggers[common.Setup], "setting exchange settings...") resp := exchange.Exchange{} for i := range cfg.CurrencySettings { @@ -474,7 +474,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange } if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(log.BackTester, "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", + log.Warnf(common.SubLoggers[common.Setup], "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", cfg.CurrencySettings[i].MaximumSlippagePercent, slippage.DefaultMaximumSlippagePercent) cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent @@ -483,7 +483,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent } if cfg.CurrencySettings[i].MinimumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(log.BackTester, "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", + log.Warnf(common.SubLoggers[common.Setup], "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", cfg.CurrencySettings[i].MinimumSlippagePercent, slippage.DefaultMinimumSlippagePercent) cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent @@ -518,7 +518,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange if limits != nil { if !cfg.CurrencySettings[i].CanUseExchangeLimits { - log.Warnf(log.BackTester, "exchange %s order execution limits supported but disabled for %s %s, live results may differ", + log.Warnf(common.SubLoggers[common.Setup], "exchange %s order execution limits supported but disabled for %s %s, live results may differ", cfg.CurrencySettings[i].ExchangeName, pair, a) @@ -576,7 +576,7 @@ func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gc exchangeBase := e.GetBase() if !exchangeBase.ValidateAPICredentials() { - log.Warnf(log.BackTester, "no credentials set for %v, this is theoretical only", exchangeBase.Name) + log.Warnf(common.SubLoggers[common.Setup], "no credentials set for %v, this is theoretical only", exchangeBase.Name) } fPair, err = exchangeBase.FormatExchangeCurrency(cp, a) @@ -596,7 +596,7 @@ func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency. Amount: 1, }) if err != nil { - log.Errorf(log.BackTester, "Could not retrieve taker fee for %v. %v", exch.GetName(), err) + log.Errorf(common.SubLoggers[common.Setup], "Could not retrieve taker fee for %v. %v", exch.GetName(), err) } fMakerFee, err := exch.GetFeeByType(ctx, @@ -608,7 +608,7 @@ func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency. Amount: 1, }) if err != nil { - log.Errorf(log.BackTester, "Could not retrieve maker fee for %v. %v", exch.GetName(), err) + log.Errorf(common.SubLoggers[common.Setup], "Could not retrieve maker fee for %v. %v", exch.GetName(), err) } return decimal.NewFromFloat(fMakerFee), decimal.NewFromFloat(fTakerFee) @@ -641,7 +641,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, return nil, err } - log.Infof(log.BackTester, "loading data for %v %v %v...\n", exch.GetName(), a, fPair) + log.Infof(common.SubLoggers[common.Setup], "loading data for %v %v %v...\n", exch.GetName(), a, fPair) resp := &kline.DataFromKline{} switch { case cfg.DataSettings.CSVData != nil: @@ -673,7 +673,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) summary := resp.RangeHolder.DataSummary(false) if len(summary) > 0 { - log.Warnf(log.BackTester, "%v", summary) + log.Warnf(common.SubLoggers[common.Setup], "%v", summary) } case cfg.DataSettings.DatabaseData != nil: if cfg.DataSettings.DatabaseData.InclusiveEndDate { @@ -694,7 +694,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, defer func() { stopErr := bt.databaseManager.Stop() if stopErr != nil { - log.Error(log.BackTester, stopErr) + log.Error(common.SubLoggers[common.Setup], stopErr) } }() resp, err = loadDatabaseData(cfg, exch.GetName(), fPair, a, dataType, isUSDTrackingPair) @@ -716,7 +716,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) summary := resp.RangeHolder.DataSummary(false) if len(summary) > 0 { - log.Warnf(log.BackTester, "%v", summary) + log.Warnf(common.SubLoggers[common.Setup], "%v", summary) } case cfg.DataSettings.APIData != nil: if cfg.DataSettings.APIData.InclusiveEndDate { @@ -830,7 +830,7 @@ func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair curren dates.SetHasDataFromCandles(candles.Candles) summary := dates.DataSummary(false) if len(summary) > 0 { - log.Warnf(log.BackTester, "%v", summary) + log.Warnf(common.SubLoggers[common.Setup], "%v", summary) } candles.FillMissingDataWithEmptyEntries(dates) candles.RemoveOutsideRange(cfg.DataSettings.APIData.StartDate, cfg.DataSettings.APIData.EndDate) @@ -866,7 +866,7 @@ func loadLiveData(cfg *config.Config, base *gctexchange.Base) error { validated := base.ValidateAPICredentials() base.API.AuthenticatedSupport = validated if !validated && cfg.DataSettings.LiveData.RealOrders { - log.Warn(log.BackTester, "invalid API credentials set, real orders set to false") + log.Warn(common.SubLoggers[common.Setup], "invalid API credentials set, real orders set to false") cfg.DataSettings.LiveData.RealOrders = false } return nil diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 05d83b053c6..c738590629f 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -265,13 +265,13 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (fill.E err = p.setHoldingsForOffset(&h, false) } if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Portfolio], err) } } err = p.addComplianceSnapshot(ev) if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Portfolio], err) } ev.SetExchangeFee(decimal.Zero) diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index e7e3f0810ea..047d2b5f513 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -141,81 +141,81 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. last.Holdings.TotalValueLost = last.Holdings.TotalValueLostToSlippage.Add(last.Holdings.TotalValueLostToVolumeSizing) sep := fmt.Sprintf("%v %v %v |\t", e, a, p) currStr := fmt.Sprintf("------------------Stats for %v %v %v------------------------------------------", e, a, p) - log.Infof(log.BackTester, currStr[:61]) - log.Infof(log.BackTester, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) - log.Infof(log.BackTester, "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) - log.Infof(log.BackTester, "%s Buy value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ",")) - log.Infof(log.BackTester, "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) - log.Infof(log.BackTester, "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) - log.Infof(log.BackTester, "%s Sell value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ",")) - log.Infof(log.BackTester, "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) - log.Infof(log.BackTester, "%s Total orders: %s\n\n", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], currStr[:61]) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total orders: %s\n\n", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) - log.Info(log.BackTester, "------------------Max Drawdown-------------------------------") - log.Infof(log.BackTester, "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time) - log.Infof(log.BackTester, "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time) - log.Infof(log.BackTester, "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ",")) - log.Infof(log.BackTester, "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ",")) - log.Infof(log.BackTester, "%s Drawdown length: %s\n\n", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ",")) + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Max Drawdown-------------------------------") + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Drawdown length: %s\n\n", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ",")) if !usingExchangeLevelFunding { - log.Info(log.BackTester, "------------------Ratios------------------------------------------------") - log.Info(log.BackTester, "------------------Rates-------------------------------------------------") - log.Infof(log.BackTester, "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ",")) - log.Info(log.BackTester, "------------------Arithmetic--------------------------------------------") + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Ratios------------------------------------------------") + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Rates-------------------------------------------------") + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ",")) + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Arithmetic--------------------------------------------") if c.ShowMissingDataWarning { - log.Infoln(log.BackTester, "Missing data was detected during this backtesting run") - log.Infoln(log.BackTester, "Ratio calculations will be skewed") + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") } - log.Infof(log.BackTester, "%s Sharpe ratio: %v", sep, c.ArithmeticRatios.SharpeRatio.Round(4)) - log.Infof(log.BackTester, "%s Sortino ratio: %v", sep, c.ArithmeticRatios.SortinoRatio.Round(4)) - log.Infof(log.BackTester, "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4)) - log.Infof(log.BackTester, "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.ArithmeticRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.ArithmeticRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4)) - log.Info(log.BackTester, "------------------Geometric--------------------------------------------") + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Geometric--------------------------------------------") if c.ShowMissingDataWarning { - log.Infoln(log.BackTester, "Missing data was detected during this backtesting run") - log.Infoln(log.BackTester, "Ratio calculations will be skewed") + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") } - log.Infof(log.BackTester, "%s Sharpe ratio: %v", sep, c.GeometricRatios.SharpeRatio.Round(4)) - log.Infof(log.BackTester, "%s Sortino ratio: %v", sep, c.GeometricRatios.SortinoRatio.Round(4)) - log.Infof(log.BackTester, "%s Information ratio: %v", sep, c.GeometricRatios.InformationRatio.Round(4)) - log.Infof(log.BackTester, "%s Calmar ratio: %v\n\n", sep, c.GeometricRatios.CalmarRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.GeometricRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.GeometricRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.GeometricRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v\n\n", sep, c.GeometricRatios.CalmarRatio.Round(4)) } - log.Info(log.BackTester, "------------------Results------------------------------------") - log.Infof(log.BackTester, "%s Starting Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice, 8, ".", ",")) - log.Infof(log.BackTester, "%s Finishing Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice, 8, ".", ",")) - log.Infof(log.BackTester, "%s Lowest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice, 8, ".", ",")) - log.Infof(log.BackTester, "%s Highest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice, 8, ".", ",")) + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Results------------------------------------") + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Starting Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Finishing Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice, 8, ".", ",")) - log.Infof(log.BackTester, "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) if !usingExchangeLevelFunding { - log.Infof(log.BackTester, "%s Strategy movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.StrategyMovement, 2, ".", ",")) - log.Infof(log.BackTester, "%s Did it beat the market: %v", sep, c.StrategyMovement.GreaterThan(c.MarketMovement)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Strategy movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.StrategyMovement, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Did it beat the market: %v", sep, c.StrategyMovement.GreaterThan(c.MarketMovement)) } - log.Infof(log.BackTester, "%s Value lost to volume sizing: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToVolumeSizing, 2, ".", ",")) - log.Infof(log.BackTester, "%s Value lost to slippage: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToSlippage, 2, ".", ",")) - log.Infof(log.BackTester, "%s Total Value lost: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLost, 2, ".", ",")) - log.Infof(log.BackTester, "%s Total Fees: %s\n\n", sep, convert.DecimalToHumanFriendlyString(c.TotalFees, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to volume sizing: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToVolumeSizing, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to slippage: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToSlippage, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Value lost: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLost, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Fees: %s\n\n", sep, convert.DecimalToHumanFriendlyString(c.TotalFees, 8, ".", ",")) - log.Infof(log.BackTester, "%s Final holdings value: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalAssetValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings value: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalAssetValue, 8, ".", ",")) if !usingExchangeLevelFunding { // the following have no direct translation to individual exchange level funds as they // combine base and quote values - log.Infof(log.BackTester, "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.QuoteSize, 8, ".", ",")) - log.Infof(log.BackTester, "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) - log.Infof(log.BackTester, "%s Final total value: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.QuoteSize, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final total value: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) } if last.PNL != nil { - log.Infof(log.BackTester, "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) - log.Infof(log.BackTester, "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNLBeforeFees, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNLBeforeFees, 8, ".", ",")) } if len(errs) > 0 { - log.Info(log.BackTester, "------------------Errors-------------------------------------") + log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Errors-------------------------------------") for i := range errs { - log.Error(log.BackTester, errs[i].Error()) + log.Error(common.SubLoggers[common.CurrencyStatistics], errs[i].Error()) } } } @@ -247,7 +247,7 @@ func CalculateBiggestEventDrawdown(closePrices []common.DataEventHandler) (Swing } intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[i].GetInterval(), 0) if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.CurrencyStatistics], err) continue } if highestPrice.IsPositive() && lowestPrice.IsPositive() { @@ -380,7 +380,7 @@ func CalculateBiggestValueAtTimeDrawdown(closePrices []ValueAtTime, interval gct } intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0) if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.CurrencyStatistics], err) } drawdownPercent := decimal.Zero if highestPrice.GreaterThan(decimal.Zero) { diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index f936e36cb2f..efb5aa4a774 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -233,73 +233,73 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { if f.Report == nil { return fmt.Errorf("%w requires report to be generated", common.ErrNilArguments) } - log.Info(log.BackTester, "------------------Funding------------------------------------") - log.Info(log.BackTester, "------------------Funding Item Results-----------------------") + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding------------------------------------") + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding Item Results-----------------------") for i := range f.Report.Items { sep := fmt.Sprintf("%v %v %v |\t", f.Report.Items[i].Exchange, f.Report.Items[i].Asset, f.Report.Items[i].Currency) if !f.Report.Items[i].PairedWith.IsEmpty() { - log.Infof(log.BackTester, "%s Paired with: %v", sep, f.Report.Items[i].PairedWith) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Paired with: %v", sep, f.Report.Items[i].PairedWith) } - log.Infof(log.BackTester, "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].InitialFunds, 8, ".", ",")) - log.Infof(log.BackTester, "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].FinalFunds, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].InitialFunds, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].FinalFunds, 8, ".", ",")) if !f.Report.DisableUSDTracking && f.Report.UsingExchangeLevelFunding { - log.Infof(log.BackTester, "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].USDInitialFunds, 2, ".", ",")) - log.Infof(log.BackTester, "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].USDFinalFunds, 2, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].USDInitialFunds, 2, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].USDFinalFunds, 2, ".", ",")) } if f.Report.Items[i].ShowInfinite { - log.Infof(log.BackTester, "%s Difference: ∞%%", sep) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: ∞%%", sep) } else { - log.Infof(log.BackTester, "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].Difference, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].Difference, 8, ".", ",")) } if f.Report.Items[i].TransferFee.GreaterThan(decimal.Zero) { - log.Infof(log.BackTester, "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].TransferFee, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].TransferFee, 8, ".", ",")) } - log.Info(log.BackTester, "") + log.Info(common.SubLoggers[common.FundingStatistics], "") } if f.Report.DisableUSDTracking { return nil } - log.Info(log.BackTester, "------------------USD Tracking Totals------------------------") + log.Info(common.SubLoggers[common.FundingStatistics], "------------------USD Tracking Totals------------------------") sep := "USD Tracking Total |\t" - log.Infof(log.BackTester, "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) - log.Infof(log.BackTester, "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) - log.Infof(log.BackTester, "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) - log.Infof(log.BackTester, "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) - log.Infof(log.BackTester, "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) - log.Infof(log.BackTester, "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) - log.Infof(log.BackTester, "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) - log.Infof(log.BackTester, "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) - log.Infof(log.BackTester, "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) - log.Infof(log.BackTester, "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) - log.Infof(log.BackTester, "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) - log.Info(log.BackTester, "------------------Ratios------------------------------------------------") - log.Info(log.BackTester, "------------------Rates-------------------------------------------------") - log.Infof(log.BackTester, "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ",")) - log.Infof(log.BackTester, "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ",")) + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Ratios------------------------------------------------") + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Rates-------------------------------------------------") + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ",")) if f.TotalUSDStatistics.ArithmeticRatios == nil || f.TotalUSDStatistics.GeometricRatios == nil { return fmt.Errorf("%w missing ratio calculations", common.ErrNilArguments) } - log.Info(log.BackTester, "------------------Arithmetic--------------------------------------------") + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Arithmetic--------------------------------------------") if wasAnyDataMissing { - log.Infoln(log.BackTester, "Missing data was detected during this backtesting run") - log.Infoln(log.BackTester, "Ratio calculations will be skewed") + log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") } - log.Infof(log.BackTester, "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SharpeRatio.Round(4)) - log.Infof(log.BackTester, "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SortinoRatio.Round(4)) - log.Infof(log.BackTester, "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4)) - log.Infof(log.BackTester, "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4)) - log.Info(log.BackTester, "------------------Geometric--------------------------------------------") + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Geometric--------------------------------------------") if wasAnyDataMissing { - log.Infoln(log.BackTester, "Missing data was detected during this backtesting run") - log.Infoln(log.BackTester, "Ratio calculations will be skewed") + log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") } - log.Infof(log.BackTester, "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SharpeRatio.Round(4)) - log.Infof(log.BackTester, "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SortinoRatio.Round(4)) - log.Infof(log.BackTester, "%s Information ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.InformationRatio.Round(4)) - log.Infof(log.BackTester, "%s Calmar ratio: %v\n\n", sep, f.TotalUSDStatistics.GeometricRatios.CalmarRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v\n\n", sep, f.TotalUSDStatistics.GeometricRatios.CalmarRatio.Round(4)) return nil } diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index ea2b6e09719..1fb2859836f 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -178,7 +178,7 @@ func (s *Statistic) AddComplianceSnapshotForTime(c compliance.Snapshot, e fill.E // CalculateAllResults calculates the statistics of all exchange asset pair holdings, // orders, ratios and drawdowns func (s *Statistic) CalculateAllResults() error { - log.Info(log.BackTester, "calculating backtesting results") + log.Info(common.SubLoggers[common.Statistics], "calculating backtesting results") s.PrintAllEventsChronologically() currCount := 0 var finalResults []FinalResultsHolder @@ -190,7 +190,7 @@ func (s *Statistic) CalculateAllResults() error { last := stats.Events[len(stats.Events)-1] err = stats.CalculateResults(s.RiskFreeRate) if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Statistics], err) } stats.PrintResults(exchangeName, assetItem, pair, s.FundManager.IsUsingExchangeLevelFunding()) stats.FinalHoldings = last.Holdings @@ -237,32 +237,32 @@ func (s *Statistic) CalculateAllResults() error { // PrintTotalResults outputs all results to the CMD func (s *Statistic) PrintTotalResults() { - log.Info(log.BackTester, "------------------Strategy-----------------------------------") - log.Infof(log.BackTester, "Strategy Name: %v", s.StrategyName) - log.Infof(log.BackTester, "Strategy Nickname: %v", s.StrategyNickname) - log.Infof(log.BackTester, "Strategy Goal: %v\n\n", s.StrategyGoal) + log.Info(common.SubLoggers[common.Statistics], "------------------Strategy-----------------------------------") + log.Infof(common.SubLoggers[common.Statistics], "Strategy Name: %v", s.StrategyName) + log.Infof(common.SubLoggers[common.Statistics], "Strategy Nickname: %v", s.StrategyNickname) + log.Infof(common.SubLoggers[common.Statistics], "Strategy Goal: %v\n\n", s.StrategyGoal) - log.Info(log.BackTester, "------------------Total Results------------------------------") - log.Info(log.BackTester, "------------------Orders-------------------------------------") - log.Infof(log.BackTester, "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) - log.Infof(log.BackTester, "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) - log.Infof(log.BackTester, "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) + log.Info(common.SubLoggers[common.Statistics], "------------------Total Results------------------------------") + log.Info(common.SubLoggers[common.Statistics], "------------------Orders-------------------------------------") + log.Infof(common.SubLoggers[common.Statistics], "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) + log.Infof(common.SubLoggers[common.Statistics], "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) + log.Infof(common.SubLoggers[common.Statistics], "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) if s.BiggestDrawdown != nil { - log.Info(log.BackTester, "------------------Biggest Drawdown-----------------------") - log.Infof(log.BackTester, "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair) - log.Infof(log.BackTester, "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ",")) - log.Infof(log.BackTester, "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time) - log.Infof(log.BackTester, "Lowest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Lowest.Value, 8, ".", ",")) - log.Infof(log.BackTester, "Lowest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Lowest.Time) - log.Infof(log.BackTester, "Calculated Drawdown: %s%%", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.DrawdownPercent, 2, ".", ",")) - log.Infof(log.BackTester, "Difference: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value.Sub(s.BiggestDrawdown.MaxDrawdown.Lowest.Value), 8, ".", ",")) - log.Infof(log.BackTester, "Drawdown length: %v\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ",")) + log.Info(common.SubLoggers[common.Statistics], "------------------Biggest Drawdown-----------------------") + log.Infof(common.SubLoggers[common.Statistics], "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair) + log.Infof(common.SubLoggers[common.Statistics], "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time) + log.Infof(common.SubLoggers[common.Statistics], "Lowest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Lowest.Value, 8, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Lowest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Lowest.Time) + log.Infof(common.SubLoggers[common.Statistics], "Calculated Drawdown: %s%%", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.DrawdownPercent, 2, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Difference: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value.Sub(s.BiggestDrawdown.MaxDrawdown.Lowest.Value), 8, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Drawdown length: %v\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ",")) } if s.BestMarketMovement != nil && s.BestStrategyResults != nil { - log.Info(log.BackTester, "------------------Orders----------------------------------") - log.Infof(log.BackTester, "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ",")) - log.Infof(log.BackTester, "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ",")) + log.Info(common.SubLoggers[common.Statistics], "------------------Orders----------------------------------") + log.Infof(common.SubLoggers[common.Statistics], "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ",")) } } @@ -319,7 +319,7 @@ func addEventOutputToTime(events []eventOutputHolder, t time.Time, message strin // grouped by time to allow a clearer picture of events func (s *Statistic) PrintAllEventsChronologically() { var results []eventOutputHolder - log.Info(log.BackTester, "------------------Events-------------------------------------") + log.Info(common.SubLoggers[common.Statistics], "------------------Events-------------------------------------") var errs gctcommon.Errors for exch, x := range s.ExchangeAssetPairStatistics { for a, y := range x { @@ -391,13 +391,13 @@ func (s *Statistic) PrintAllEventsChronologically() { }) for i := range results { for j := range results[i].Events { - log.Info(log.BackTester, results[i].Events[j]) + log.Info(common.SubLoggers[common.Statistics], results[i].Events[j]) } } if len(errs) > 0 { - log.Info(log.BackTester, "------------------Errors-------------------------------------") + log.Info(common.SubLoggers[common.Statistics], "------------------Errors-------------------------------------") for i := range errs { - log.Error(log.BackTester, errs[i].Error()) + log.Error(common.SubLoggers[common.Statistics], errs[i].Error()) } } } @@ -450,7 +450,7 @@ func CalculateRatios(benchmarkRates, returnsPerCandle []decimal.Decimal, riskFre arithmeticSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, arithmeticReturnsPerCandle) if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { if errors.Is(err, gctmath.ErrInexactConversion) { - log.Warnf(log.BackTester, "%s funding arithmetic sortino ratio %v", logMessage, err) + log.Warnf(common.SubLoggers[common.Statistics], "%s funding arithmetic sortino ratio %v", logMessage, err) } else { return nil, nil, err } @@ -485,7 +485,7 @@ func CalculateRatios(benchmarkRates, returnsPerCandle []decimal.Decimal, riskFre geomSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, geometricReturnsPerCandle) if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { if errors.Is(err, gctmath.ErrInexactConversion) { - log.Warnf(log.BackTester, "%s geometric sortino ratio %v", logMessage, err) + log.Warnf(common.SubLoggers[common.Statistics], "%s geometric sortino ratio %v", logMessage, err) } else { return nil, nil, err } diff --git a/backtester/main.go b/backtester/main.go index 458e4780f32..ce9f410fd8b 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -1,14 +1,17 @@ package main import ( + "errors" "flag" "fmt" "os" "path/filepath" + "strings" - "github.com/thrasher-corp/gocryptotrader/backtester/backtest" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/config" + backtest "github.com/thrasher-corp/gocryptotrader/backtester/engine" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/signaler" ) @@ -70,12 +73,25 @@ func main() { var bt *backtest.BackTest var cfg *config.Config log.GlobalLogConfig = log.GenDefaultSettings() + log.GlobalLogConfig.AdvancedSettings.ShowLogSystemName = convert.BoolPtr(true) err = log.SetupGlobalLogger() if err != nil { fmt.Printf("Could not setup global logger. Error: %v.\n", err) os.Exit(1) } + for k := range common.SubLoggers { + common.SubLoggers[k], err = log.NewSubLogger(k) + if err != nil { + if errors.Is(err, log.ErrSubLoggerAlreadyRegistered) { + common.SubLoggers[k] = log.SubLoggers[strings.ToUpper(k)] + continue + } + fmt.Printf("Could not setup global logger. Error: %v.\n", err) + os.Exit(1) + } + } + cfg, err = config.ReadConfigFromFile(configPath) if err != nil { fmt.Printf("Could not read config. Error: %v.\n", err) @@ -116,7 +132,7 @@ func main() { err = bt.Statistic.CalculateAllResults() if err != nil { - log.Error(log.BackTester, err) + log.Error(log.Global, err) os.Exit(1) } @@ -124,7 +140,7 @@ func main() { bt.Reports.UseDarkMode(darkReport) err = bt.Reports.GenerateReport() if err != nil { - log.Error(log.BackTester, err) + log.Error(log.Global, err) } } } diff --git a/backtester/report/report.go b/backtester/report/report.go index a346d1116b2..ffdbbea1fc3 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -18,7 +18,7 @@ import ( // GenerateReport sends final data from statistics to a template // to create a lovely final report for someone to view func (d *Data) GenerateReport() error { - log.Info(log.BackTester, "generating report") + log.Info(common.SubLoggers[common.Report], "generating report") err := d.enhanceCandles() if err != nil { return err @@ -71,7 +71,7 @@ func (d *Data) GenerateReport() error { defer func() { err = f.Close() if err != nil { - log.Error(log.BackTester, err) + log.Error(common.SubLoggers[common.Report], err) } }() @@ -79,7 +79,7 @@ func (d *Data) GenerateReport() error { if err != nil { return err } - log.Infof(log.BackTester, "successfully saved report to %v", filepath.Join(d.OutputPath, fileName)) + log.Infof(common.SubLoggers[common.Report], "successfully saved report to %v", filepath.Join(d.OutputPath, fileName)) return nil } diff --git a/cmd/documentation/backtester_templates/backtester_backtest_readme.tmpl b/cmd/documentation/backtester_templates/backtester_engine_readme.tmpl similarity index 77% rename from cmd/documentation/backtester_templates/backtester_backtest_readme.tmpl rename to cmd/documentation/backtester_templates/backtester_engine_readme.tmpl index 88c6c1fac90..105c017db10 100644 --- a/cmd/documentation/backtester_templates/backtester_backtest_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_engine_readme.tmpl @@ -1,8 +1,8 @@ -{{define "backtester backtest" -}} +{{define "backtester engine" -}} {{template "backtester-header" .}} ## {{.CapitalName}} package overview -The backtest package is the most important package of the GoCryptoTrader backtester. It is the engine which combines all elements. +The engine package is the most important package of the GoCryptoTrader backtester. It is the engine which combines all elements. It is responsible for the following functionality - Loading settings from a provided config file - Retrieving data diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 63a2b3c4b34..b01b40a9d5e 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -12,7 +12,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -573,9 +572,6 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { result.RealisedPNLBeforeFees = decimal.Zero p.status = Closed } - if result.RealisedPNLBeforeFees.IsZero() && result.UnrealisedPNL.IsZero() { - log.Debug(log.BackTester, "woah nelly") - } p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) if err != nil { return err diff --git a/log/logger.go b/log/logger.go index af50c5fc4a6..e353e40bf1a 100644 --- a/log/logger.go +++ b/log/logger.go @@ -8,8 +8,9 @@ import ( ) var ( - errEmptyLoggerName = errors.New("cannot have empty logger name") - errSubLoggerAlreadyregistered = errors.New("sub logger already registered") + errEmptyLoggerName = errors.New("cannot have empty logger name") + // ErrSubLoggerAlreadyRegistered Returned when a sublogger is registered multiple times + ErrSubLoggerAlreadyRegistered = errors.New("sub logger already registered") ) func newLogger(c *Config) Logger { diff --git a/log/logger_setup.go b/log/logger_setup.go index a2785fff199..0b2d6023859 100644 --- a/log/logger_setup.go +++ b/log/logger_setup.go @@ -157,7 +157,6 @@ func init() { Global = registerNewSubLogger("LOG") ConnectionMgr = registerNewSubLogger("CONNECTION") - BackTester = registerNewSubLogger("BACKTESTER") CommunicationMgr = registerNewSubLogger("COMMS") APIServerMgr = registerNewSubLogger("API") ConfigMgr = registerNewSubLogger("CONFIG") diff --git a/log/logger_test.go b/log/logger_test.go index 6e6d4fcc7ef..d441ceb2215 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -581,8 +581,8 @@ func TestNewSubLogger(t *testing.T) { Debug(sl, "testerinos") _, err = NewSubLogger("TESTERINOS") - if !errors.Is(err, errSubLoggerAlreadyregistered) { - t.Fatalf("received: %v but expected: %v", err, errSubLoggerAlreadyregistered) + if !errors.Is(err, ErrSubLoggerAlreadyRegistered) { + t.Fatalf("received: %v but expected: %v", err, ErrSubLoggerAlreadyRegistered) } } diff --git a/log/sublogger.go b/log/sublogger.go index 96df5b4f611..04aa587df37 100644 --- a/log/sublogger.go +++ b/log/sublogger.go @@ -14,7 +14,7 @@ func NewSubLogger(name string) (*SubLogger, error) { RWM.RLock() if _, ok := SubLoggers[name]; ok { RWM.RUnlock() - return nil, errSubLoggerAlreadyregistered + return nil, ErrSubLoggerAlreadyRegistered } RWM.RUnlock() return registerNewSubLogger(name), nil diff --git a/log/sublogger_types.go b/log/sublogger_types.go index 76a36d3239c..1328af6045f 100644 --- a/log/sublogger_types.go +++ b/log/sublogger_types.go @@ -10,7 +10,6 @@ var ( SubLoggers = map[string]*SubLogger{} Global *SubLogger - BackTester *SubLogger ConnectionMgr *SubLogger CommunicationMgr *SubLogger APIServerMgr *SubLogger From dc60eec40b0c7803c9477544f1bae2e61cf8fd04 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 24 Feb 2022 15:32:11 +1100 Subject: [PATCH 083/171] Begins attempt at automatically handling contracts and collateral based on direction --- backtester/engine/backtest.go | 2 +- backtester/engine/backtest_test.go | 2 +- backtester/engine/backtest_types.go | 5 ++- backtester/engine/live.go | 2 +- backtester/engine/setup.go | 2 +- backtester/eventhandlers/exchange/exchange.go | 6 ++-- .../eventhandlers/portfolio/portfolio.go | 3 +- backtester/funding/collateral.go | 31 +++++++++++++++++-- backtester/funding/funding_types.go | 7 +++-- exchanges/order/futures.go | 3 +- 10 files changed, 50 insertions(+), 13 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 0986313e0bb..2b38071f800 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -1,4 +1,4 @@ -package backtest +package engine import ( "errors" diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 9cec6552a2f..79e99970cbe 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -1,4 +1,4 @@ -package backtest +package engine import ( "errors" diff --git a/backtester/engine/backtest_types.go b/backtester/engine/backtest_types.go index c6d9266ccf4..1ef296c9bbe 100644 --- a/backtester/engine/backtest_types.go +++ b/backtester/engine/backtest_types.go @@ -1,4 +1,4 @@ -package backtest +package engine import ( "errors" @@ -43,3 +43,6 @@ type BackTest struct { orderManager *engine.OrderManager databaseManager *engine.DatabaseConnectionManager } + +// TODO make this a config var +const defaultRounding = 8 diff --git a/backtester/engine/live.go b/backtester/engine/live.go index 62c79934492..c4e299a1a04 100644 --- a/backtester/engine/live.go +++ b/backtester/engine/live.go @@ -1,4 +1,4 @@ -package backtest +package engine import ( "context" diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index 6298f733068..a6cd3b57174 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -1,4 +1,4 @@ -package backtest +package engine import ( "context" diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 0bbb36c5d51..fe4953ac3e6 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -44,7 +44,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ClosePrice: data.Latest().GetClosePrice(), FillDependentEvent: o.GetFillDependentEvent(), } - if o.GetAssetType().IsFutures() && o.GetDirection() != common.ClosePosition { + if o.GetAssetType().IsFutures() && !o.IsClosingPosition() { f.Amount = o.GetAllocatedFunds() } eventFunds := o.GetAllocatedFunds() @@ -52,7 +52,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if err != nil { return f, err } - f.ExchangeFee = cs.ExchangeFee // defaulting to just using taker fee right now without orderbook + f.ExchangeFee = cs.ExchangeFee f.Direction = o.GetDirection() highStr := data.StreamHigh() high := highStr[len(highStr)-1] @@ -231,6 +231,8 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c case gctorder.Short: minMax = cs.SellSide direction = common.CouldNotShort + case common.ClosePosition: + return nil default: direction = f.GetDirection() f.SetDirection(common.DoNothing) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index c738590629f..11aab8c4eda 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -215,7 +215,8 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi case gctorder.Sell: err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) sizedOrder.AllocatedFunds = sizedOrder.Amount - case gctorder.Short, gctorder.Long: + case gctorder.Short, gctorder.Long, common.ClosePosition: + err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) sizedOrder.AllocatedFunds = sizedOrder.Amount.Div(sizedOrder.Price) default: diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 5d50d2b76c8..47e4ed25111 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -15,6 +15,9 @@ var ( ErrNilPair = errors.New("nil pair") ) +// TODO consider moving futures tracking to funding +// we're already passing around funding items, it can then also have all the lovely tracking attached? + func (c *Collateral) CanPlaceOrder(_ order.Side) bool { return c.Collateral.CanPlaceOrder() } @@ -56,8 +59,32 @@ func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { return c, nil } -func (c *Collateral) Reserve(amount decimal.Decimal, _ order.Side) error { - return c.Collateral.Reserve(amount) +func (c *Collateral) UpdateCollateral(s order.Side, amount, diff decimal.Decimal) error { + switch { + case c.currentDirection == nil: + c.currentDirection = &s + return c.Collateral.Reserve(amount) + case *c.currentDirection == s: + return c.Collateral.Reserve(amount) + case *c.currentDirection != s: + return c.Collateral.Release(amount, diff) + default: + return errors.New("woah nelly") + } +} + +func (c *Collateral) UpdateContracts(s order.Side, amount, diff decimal.Decimal) error { + switch { + case c.currentDirection == nil: + c.currentDirection = &s + return c.Contract.Reserve(amount) + case *c.currentDirection == s: + return c.Contract.Reserve(amount) + case *c.currentDirection != s: + return c.Contract.Release(amount, diff) + default: + return errors.New("woah nelly") + } } func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 4ef50248fff..e1d81349424 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -116,6 +116,8 @@ type IPairReleaser interface { type ICollateralReleaser interface { ICollateralReader + UpdateCollateral(s order.Side, amount, diff decimal.Decimal) error + UpdateContracts(s order.Side, amount, diff decimal.Decimal) error TakeProfit(contracts, originalPositionSize, positionReturns decimal.Decimal) error ReleaseContracts(decimal.Decimal) error Liquidate() @@ -157,8 +159,9 @@ type Pair struct { // Collateral consists of a currency pair for a futures contract // and associates it with an addition collateral pair to take funding from type Collateral struct { - Contract *Item - Collateral *Item + currentDirection *order.Side + Contract *Item + Collateral *Item } // Report holds all funding data for result reporting diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b01b40a9d5e..f178b7c40f6 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -12,6 +12,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -592,7 +593,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } else { p.exposure = shortSide.Sub(longSide) } - + log.Debugf(log.ExchangeSys, "order amount %v position exposure %v current direction %v order direction %v", d.Amount, p.exposure, p.currentDirection, d.Side) if p.exposure.IsZero() { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) From 8e106eb91c80f916cb54ae1eb23f8f26e07a7293 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 1 Mar 2022 15:18:54 +1100 Subject: [PATCH 084/171] Merge master + fixes --- .github/dependabot.yml | 7 + README.md | 6 +- ...a-api-candles-exchange-level-funding.strat | 3 +- .../config/examples/ftx-cash-carry.strat | 2 +- .../t2b2-api-candles-exchange-funding.strat | 6 +- backtester/data/data_test.go | 3 +- backtester/engine/backtest_test.go | 41 +- backtester/engine/setup.go | 14 +- backtester/eventhandlers/exchange/exchange.go | 2 +- .../eventhandlers/exchange/exchange_test.go | 37 +- .../portfolio/holdings/holdings_test.go | 12 +- .../eventhandlers/portfolio/portfolio_test.go | 22 +- .../statistics/fundingstatistics.go | 4 +- .../statistics/fundingstatistics_test.go | 24 +- .../statistics/statistics_test.go | 19 +- .../ftxcashandcarry/ftxcashandcarry.go | 14 +- .../ftxcashandcarry/ftxcashandcarry_test.go | 72 +- backtester/funding/collateral.go | 14 + backtester/funding/funding.go | 28 +- backtester/funding/funding_test.go | 30 +- backtester/funding/item.go | 2 +- .../trackingcurrencies/trackingcurrencies.go | 6 +- .../trackingcurrencies_test.go | 16 +- cmd/apichecker/apicheck.go | 13 +- cmd/documentation/currency_templates/fx.tmpl | 1 + .../root_templates/root_readme.tmpl | 2 +- .../exchange_wrapper_issues_test.go | 2 +- cmd/exchange_wrapper_issues/main.go | 4 +- cmd/gctcli/commands.go | 24 +- cmd/portfolio/portfolio.go | 48 +- cmd/portfolio/portfolio_test.go | 7 - config/config.go | 253 +- config/config_test.go | 112 +- config/config_types.go | 32 +- config_example.json | 2 +- currency/code.go | 184 +- currency/code_test.go | 229 +- currency/code_types.go | 4652 +- currency/coinmarketcap/coinmarketcap.go | 20 +- currency/coinmarketcap/coinmarketcap_test.go | 2 +- currency/coinmarketcap/coinmarketcap_types.go | 2 +- currency/conversion.go | 47 +- currency/currencies.go | 16 +- currency/currency.go | 67 +- currency/currency_test.go | 83 +- currency/currency_types.go | 66 +- currency/forexprovider/README.md | 1 + currency/forexprovider/base/base.go | 13 +- .../currencyconverterapi.go | 6 +- .../currencylayer/currencylayer.go | 7 +- .../currencylayer/currencylayer_types.go | 3 - .../exchangerate.host/exchangerate.go | 6 +- .../exchangeratesapi.io/exchangeratesapi.go | 6 +- currency/forexprovider/fixer.io/fixer.go | 11 +- currency/forexprovider/fixer.io/fixer_test.go | 5 - currency/forexprovider/forexprovider.go | 90 +- .../openexchangerates/openexchangerates.go | 6 +- currency/manager.go | 10 +- currency/manager_test.go | 8 +- currency/pair.go | 23 +- currency/pair_methods.go | 82 +- currency/pair_test.go | 79 +- currency/pairs.go | 227 +- currency/pairs_test.go | 294 +- currency/storage.go | 418 +- currency/storage_test.go | 239 +- currency/storage_types.go | 20 +- currency/symbol_test.go | 2 +- currency/translation.go | 20 +- currency/translation_test.go | 18 +- engine/currency_state_manager_test.go | 32 +- engine/engine.go | 133 +- engine/engine_test.go | 40 +- engine/engine_types.go | 3 +- engine/event_manager_test.go | 4 +- engine/exchange_manager.go | 6 +- engine/exchange_manager_test.go | 4 +- engine/helpers.go | 105 +- engine/helpers_test.go | 14 +- engine/order_manager.go | 3 +- engine/order_manager_test.go | 24 +- engine/portfolio_manager.go | 25 +- engine/rpcserver.go | 201 +- engine/rpcserver_test.go | 109 +- engine/sync_manager.go | 139 +- engine/sync_manager_test.go | 40 +- engine/sync_manager_types.go | 24 +- engine/websocketroutine_manager.go | 3 +- engine/websocketroutine_manager_test.go | 13 +- engine/websocketroutine_manager_types.go | 4 +- exchanges/account/account.go | 29 +- exchanges/account/account_test.go | 75 +- exchanges/account/account_types.go | 13 +- exchanges/alphapoint/alphapoint_wrapper.go | 17 +- exchanges/binance/binance.go | 2 +- exchanges/binance/binance_mock_test.go | 5 +- exchanges/binance/binance_test.go | 41 +- exchanges/binance/binance_types.go | 7 +- exchanges/binance/binance_wrapper.go | 63 +- exchanges/bitfinex/bitfinex_wrapper.go | 8 +- exchanges/bitflyer/bitflyer_wrapper.go | 5 +- exchanges/bithumb/bithumb_wrapper.go | 13 +- exchanges/bitmex/bitmex_test.go | 3 +- exchanges/bitmex/bitmex_wrapper.go | 31 +- exchanges/bitstamp/bitstamp_mock_test.go | 5 +- exchanges/bitstamp/bitstamp_wrapper.go | 8 +- exchanges/bittrex/bittrex_wrapper.go | 8 +- exchanges/btcmarkets/btcmarkets_wrapper.go | 11 +- exchanges/btse/btse_wrapper.go | 8 +- exchanges/coinbasepro/coinbasepro.go | 4 +- exchanges/coinbasepro/coinbasepro_wrapper.go | 29 +- exchanges/coinut/coinut.go | 2 +- exchanges/coinut/coinut_wrapper.go | 33 +- .../currencystate/currency_state_test.go | 24 +- exchanges/exchange.go | 72 +- exchanges/exchange_test.go | 118 +- exchanges/exchange_types.go | 1 - exchanges/exmo/exmo_wrapper.go | 24 +- exchanges/ftx/ftx.go | 116 +- exchanges/ftx/ftx_test.go | 245 +- exchanges/ftx/ftx_types.go | 105 +- exchanges/ftx/ftx_wrapper.go | 376 +- exchanges/gateio/gateio_test.go | 2 +- exchanges/gateio/gateio_wrapper.go | 27 +- exchanges/gemini/gemini_mock_test.go | 5 +- exchanges/gemini/gemini_wrapper.go | 16 +- exchanges/hitbtc/hitbtc_wrapper.go | 16 +- exchanges/huobi/huobi_test.go | 38 +- exchanges/huobi/huobi_wrapper.go | 49 +- exchanges/interfaces.go | 4 +- exchanges/itbit/itbit.go | 2 +- exchanges/itbit/itbit_wrapper.go | 20 +- exchanges/kraken/kraken_test.go | 4 +- exchanges/kraken/kraken_wrapper.go | 9 +- exchanges/lbank/lbank_test.go | 2 +- exchanges/lbank/lbank_wrapper.go | 11 +- .../localbitcoins/localbitcoins_mock_test.go | 5 +- .../localbitcoins/localbitcoins_wrapper.go | 18 +- exchanges/okcoin/okcoin_wrapper.go | 5 +- exchanges/okex/okex_wrapper.go | 5 +- exchanges/okgroup/okgroup_websocket.go | 2 +- exchanges/okgroup/okgroup_wrapper.go | 3 +- exchanges/order/futures.go | 196 +- exchanges/order/futures_test.go | 12 +- exchanges/order/futures_types.go | 110 +- exchanges/order/order_test.go | 6 +- exchanges/order/orders.go | 8 +- exchanges/orderbook/orderbook_test.go | 4 +- exchanges/poloniex/currency_details.go | 6 +- exchanges/poloniex/currency_details_test.go | 24 +- exchanges/poloniex/poloniex_mock_test.go | 5 +- exchanges/poloniex/poloniex_wrapper.go | 13 +- exchanges/request/client.go | 131 + exchanges/request/client_test.go | 148 + exchanges/request/limit.go | 20 +- exchanges/request/request.go | 131 +- exchanges/request/request_test.go | 163 +- exchanges/request/request_types.go | 6 +- exchanges/sharedtestvalues/customex.go | 7 +- exchanges/stats/stats.go | 4 +- exchanges/stream/websocket.go | 11 +- exchanges/stream/websocket_test.go | 22 +- exchanges/stream/websocket_types.go | 24 +- exchanges/ticker/ticker_test.go | 2 +- exchanges/yobit/yobit.go | 6 +- exchanges/yobit/yobit_test.go | 2 +- exchanges/yobit/yobit_wrapper.go | 8 +- exchanges/zb/zb_mock_test.go | 5 +- exchanges/zb/zb_test.go | 6 +- exchanges/zb/zb_wrapper.go | 8 +- gctrpc/rpc.pb.go | 4880 +- gctrpc/rpc.proto | 78 +- gctrpc/rpc.swagger.json | 124 +- gctscript/modules/gct/exchange.go | 2 +- .../wrappers/gct/exchange/exchange_test.go | 2 +- gctscript/wrappers/validator/validator.go | 4 +- .../wrappers/validator/validator_test.go | 6 +- go.mod | 10 +- go.sum | 21 +- log/logger_setup.go | 1 + log/sublogger_types.go | 1 + main.go | 3 +- portfolio/banking/banking.go | 2 +- portfolio/portfolio.go | 10 +- portfolio/portfolio_test.go | 10 +- portfolio/withdraw/validate.go | 6 +- portfolio/withdraw/validate_test.go | 11 - testdata/http_mock/binance/binance.json | 73082 +++++++++++++--- web/package-lock.json | 6 +- 189 files changed, 71007 insertions(+), 18856 deletions(-) delete mode 100644 cmd/portfolio/portfolio_test.go create mode 100644 exchanges/request/client.go create mode 100644 exchanges/request/client_test.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 92c2a6664cd..37375808aaf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,3 +6,10 @@ updates: interval: daily time: "19:30" open-pull-requests-limit: 10 +- package-ecosystem: npm + directory: "/web" + schedule: + interval: "weekly" + # deprecated for now as it requires a more in-depth overhaul + open-pull-requests-limit: 0 + diff --git a/README.md b/README.md index 784fd198d41..42c441cfed4 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ However, we welcome pull requests for any exchange which does not match this cri + Connection monitor package. + gRPC service and JSON RPC proxy. See [gRPC service](/gctrpc/README.md). + gRPC client. See [gctcli](/cmd/gctcli/README.md). -+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Fixer.io, OpenExchangeRates). ++ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Exchange Rates, Fixer.io, OpenExchangeRates, Exchange Rate Host). + Packages for handling currency pairs, tickers and orderbooks. + Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking. + Basic event trigger system. @@ -144,7 +144,7 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| | [thrasher-](https://github.com/thrasher-) | 664 | -| [shazbert](https://github.com/shazbert) | 234 | +| [shazbert](https://github.com/shazbert) | 232 | | [gloriousCode](https://github.com/gloriousCode) | 194 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | | [dependabot[bot]](https://github.com/apps/dependabot) | 57 | @@ -160,7 +160,7 @@ Binaries will be published once the codebase reaches a stable condition. | [marcofranssen](https://github.com/marcofranssen) | 8 | | [dackroyd](https://github.com/dackroyd) | 5 | | [cranktakular](https://github.com/cranktakular) | 5 | -| [khcchiu](https://github.com/khcchiu) | 5 | +| [khcchiu](https://github.com/khcchiu) | 4 | | [woshidama323](https://github.com/woshidama323) | 3 | | [yangrq1018](https://github.com/yangrq1018) | 3 | | [TaltaM](https://github.com/TaltaM) | 3 | diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index 78df483db65..b87a039999e 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -11,8 +11,7 @@ "asset": "spot", "currency": "USDT", "initial-funds": "100000", - "transfer-fee": "0", - "collateral": false + "transfer-fee": "0" } ], "disable-usd-tracking": true diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index d0a3cd3fdf7..5b47e4a9eff 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -10,7 +10,7 @@ "exchange-name": "ftx", "asset": "spot", "currency": "USD", - "initial-funds": "10000", + "initial-funds": "100000", "transfer-fee": "0" } ], diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 1d633afbee2..8052db9b484 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -11,16 +11,14 @@ "asset": "spot", "currency": "BTC", "initial-funds": "3", - "transfer-fee": "0", - "collateral": false + "transfer-fee": "0" }, { "exchange-name": "ftx", "asset": "spot", "currency": "USDT", "initial-funds": "10000", - "transfer-fee": "0", - "collateral": false + "transfer-fee": "0" } ], "disable-usd-tracking": false, diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index c9f028bce5a..8e38f66fb4b 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -60,6 +60,7 @@ func TestSetup(t *testing.T) { } } +/* func TestStream(t *testing.T) { var d Base var f fakeDataHandler @@ -108,7 +109,7 @@ func TestStream(t *testing.T) { t.Error("expected 20") } } - +*/ func TestSetDataForCurrency(t *testing.T) { t.Parallel() d := HandlerPerCurrency{} diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 79e99970cbe..e37ef8ddbbf 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -47,13 +47,13 @@ func TestMain(m *testing.M) { func TestNewFromConfig(t *testing.T) { t.Parallel() - _, err := NewFromConfig(nil, "", "") + _, err := NewFromConfig(nil, "", "", false) if !errors.Is(err, errNilConfig) { t.Errorf("received %v, expected %v", err, errNilConfig) } cfg := &config.Config{} - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, base.ErrStrategyNotFound) { t.Errorf("received: %v, expected: %v", err, base.ErrStrategyNotFound) } @@ -65,17 +65,17 @@ func TestNewFromConfig(t *testing.T) { Quote: "test", }, } - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, engine.ErrExchangeNotFound) { t.Errorf("received: %v, expected: %v", err, engine.ErrExchangeNotFound) } cfg.CurrencySettings[0].ExchangeName = testExchange - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, errInvalidConfigAsset) { t.Errorf("received: %v, expected: %v", err, errInvalidConfigAsset) } cfg.CurrencySettings[0].Asset = asset.Spot.String() - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, base.ErrStrategyNotFound) { t.Errorf("received: %v, expected: %v", err, base.ErrStrategyNotFound) } @@ -93,19 +93,19 @@ func TestNewFromConfig(t *testing.T) { EndDate: time.Time{}, } - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if err != nil && !strings.Contains(err.Error(), "unrecognised dataType") { t.Error(err) } cfg.DataSettings.DataType = common.CandleStr - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, errIntervalUnset) { t.Errorf("received: %v, expected: %v", err, errIntervalUnset) } cfg.DataSettings.Interval = gctkline.OneMin.Duration() cfg.CurrencySettings[0].MakerFee = decimal.Zero cfg.CurrencySettings[0].TakerFee = decimal.Zero - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, gctcommon.ErrDateUnset) { t.Errorf("received: %v, expected: %v", err, gctcommon.ErrDateUnset) } @@ -113,7 +113,7 @@ func TestNewFromConfig(t *testing.T) { cfg.DataSettings.APIData.StartDate = time.Now().Add(-time.Minute) cfg.DataSettings.APIData.EndDate = time.Now() cfg.DataSettings.APIData.InclusiveEndDate = true - _, err = NewFromConfig(cfg, "", "") + _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -429,7 +429,10 @@ func TestLoadLiveData(t *testing.T) { func TestReset(t *testing.T) { t.Parallel() - f := funding.SetupFundingManager(true, false) + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, true, false) + if err != nil { + t.Error(err) + } bt := BackTest{ shutdown: make(chan struct{}), Datas: &data.HandlerPerCurrency{}, @@ -472,12 +475,15 @@ func TestFullCycle(t *testing.T) { if err != nil { t.Error(err) } - f := funding.SetupFundingManager(false, true) - b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero, false) + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) if err != nil { t.Error(err) } - quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero, false) + b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero) + if err != nil { + t.Error(err) + } + quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Error(err) } @@ -577,12 +583,15 @@ func TestFullCycleMulti(t *testing.T) { if err != nil { t.Error(err) } - f := funding.SetupFundingManager(false, true) - b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero, false) + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) + if err != nil { + t.Error(err) + } + b, err := funding.CreateItem(ex, a, cp.Base, decimal.Zero, decimal.Zero) if err != nil { t.Error(err) } - quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero, false) + quote, err := funding.CreateItem(ex, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Error(err) } diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index a6cd3b57174..0e6178cc067 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -87,11 +87,15 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool SellSide: sellRule, } - funds := funding.SetupFundingManager( + funds, err := funding.SetupFundingManager( bt.exchangeManager, cfg.StrategySettings.UseExchangeLevelFunding, cfg.StrategySettings.DisableUSDTracking, ) + if err != nil { + return nil, err + } + if cfg.StrategySettings.UseExchangeLevelFunding { for i := range cfg.StrategySettings.ExchangeLevelFunding { var a asset.Item @@ -559,19 +563,19 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { e, err := bt.exchangeManager.GetExchangeByName(exch) if err != nil { - return nil, currency.Pair{}, "", err + return nil, currency.EMPTYPAIR, "", err } var cp, fPair currency.Pair cp, err = currency.NewPairFromStrings(base, quote) if err != nil { - return nil, currency.Pair{}, "", err + return nil, currency.EMPTYPAIR, "", err } var a asset.Item a, err = asset.New(ass) if err != nil { - return nil, currency.Pair{}, "", err + return nil, currency.EMPTYPAIR, "", err } exchangeBase := e.GetBase() @@ -581,7 +585,7 @@ func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gc fPair, err = exchangeBase.FormatExchangeCurrency(cp, a) if err != nil { - return nil, currency.Pair{}, "", err + return nil, currency.EMPTYPAIR, "", err } return e, fPair, a, nil } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index fe4953ac3e6..3adbb0d9099 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -351,7 +351,7 @@ func (e *Exchange) SetExchangeAssetCurrencySettings(a asset.Item, cp currency.Pa } for i := range e.CurrencySettings { - if e.CurrencySettings[i].Pair == cp && + if e.CurrencySettings[i].Pair.Equal(cp) && e.CurrencySettings[i].Asset == a && strings.EqualFold(c.Exchange.GetName(), e.CurrencySettings[i].Exchange.GetName()) { e.CurrencySettings[i] = *c diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 0d0f065e3ad..c4b5b356068 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -36,11 +36,11 @@ func (f *fakeFund) GetCollateralReader() (funding.ICollateralReader, error) { } func (f *fakeFund) GetPairReleaser() (funding.IPairReleaser, error) { - btc, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(9999), decimal.NewFromInt(9999), false) + btc, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(9999), decimal.NewFromInt(9999)) if err != nil { return nil, err } - usd, err := funding.CreateItem(testExchange, asset.Spot, currency.USD, decimal.NewFromInt(9999), decimal.NewFromInt(9999), false) + usd, err := funding.CreateItem(testExchange, asset.Spot, currency.USD, decimal.NewFromInt(9999), decimal.NewFromInt(9999)) if err != nil { return nil, err } @@ -81,7 +81,7 @@ func TestReset(t *testing.T) { func TestSetCurrency(t *testing.T) { t.Parallel() e := Exchange{} - e.SetExchangeAssetCurrencySettings("", currency.Pair{}, &Settings{}) + e.SetExchangeAssetCurrencySettings("", currency.EMPTYPAIR, &Settings{}) if len(e.CurrencySettings) != 0 { t.Error("expected 0") } @@ -146,35 +146,6 @@ func TestCalculateExchangeFee(t *testing.T) { } } -func TestSizeOrder(t *testing.T) { - t.Parallel() - e := Exchange{} - _, _, err := e.sizeOfflineOrder(decimal.Zero, decimal.Zero, decimal.Zero, nil, nil) - if !errors.Is(err, common.ErrNilArguments) { - t.Error(err) - } - cs := &Settings{} - f := &fill.Fill{ - ClosePrice: decimal.NewFromInt(1337), - Amount: decimal.NewFromInt(1), - } - _, _, err = e.sizeOfflineOrder(decimal.Zero, decimal.Zero, decimal.Zero, cs, f) - if !errors.Is(err, errDataMayBeIncorrect) { - t.Errorf("received: %v, expected: %v", err, errDataMayBeIncorrect) - } - var p, a decimal.Decimal - p, a, err = e.sizeOfflineOrder(decimal.NewFromInt(10), decimal.NewFromInt(2), decimal.NewFromInt(10), cs, f) - if err != nil { - t.Error(err) - } - if !p.Equal(decimal.NewFromInt(10)) { - t.Error("expected 10") - } - if !a.Equal(decimal.NewFromInt(1)) { - t.Error("expected 1") - } -} - func TestPlaceOrder(t *testing.T) { t.Parallel() bot := &engine.Engine{} @@ -419,7 +390,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { d := &kline.DataFromKline{ Item: gctkline.Item{ Exchange: "", - Pair: currency.Pair{}, + Pair: currency.EMPTYPAIR, Asset: "", Interval: 0, Candles: []gctkline.Candle{ diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index 87dbe591823..e4a6097e041 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -23,11 +23,11 @@ const ( func pair(t *testing.T) *funding.Pair { t.Helper() - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero, false) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero, false) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Fatal(err) } @@ -91,11 +91,11 @@ func TestUpdateValue(t *testing.T) { func TestUpdateBuyStats(t *testing.T) { t.Parallel() - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) if err != nil { t.Fatal(err) } @@ -222,11 +222,11 @@ func TestUpdateBuyStats(t *testing.T) { func TestUpdateSellStats(t *testing.T) { t.Parallel() - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) if err != nil { t.Fatal(err) } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 419b9fb7c69..ddf2c80436a 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -264,11 +264,11 @@ func TestUpdate(t *testing.T) { if !errors.Is(err, funding.ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, funding.ErrFundsNotFound) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) if err != nil { t.Fatal(err) } @@ -315,7 +315,7 @@ func TestUpdate(t *testing.T) { func TestGetFee(t *testing.T) { t.Parallel() p := Portfolio{} - f := p.GetFee("", "", currency.Pair{}) + f := p.GetFee("", "", currency.EMPTYPAIR) if !f.IsZero() { t.Error("expected 0") } @@ -337,7 +337,7 @@ func TestGetFee(t *testing.T) { func TestGetComplianceManager(t *testing.T) { t.Parallel() p := Portfolio{} - _, err := p.GetComplianceManager("", "", currency.Pair{}) + _, err := p.GetComplianceManager("", "", currency.EMPTYPAIR) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } @@ -426,11 +426,11 @@ func TestOnFill(t *testing.T) { t.Error(err) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero, false) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero, false) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) if err != nil { t.Fatal(err) } @@ -476,11 +476,11 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, funding.ErrFundsNotFound) { t.Errorf("received: %v, expected: %v", err, funding.ErrFundsNotFound) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero, false) + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero, false) + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Fatal(err) } @@ -711,7 +711,7 @@ func TestGetLatestSnapshot(t *testing.T) { func TestCalculatePNL(t *testing.T) { p := &Portfolio{} ev := &kline.Kline{} - err := p.UpdatePNL(ev) + err := p.UpdatePNL(ev, decimal.Zero) if !errors.Is(err, gctorder.ErrNotFutureAsset) { t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFutureAsset) } @@ -736,7 +736,7 @@ func TestCalculatePNL(t *testing.T) { ev.CurrencyPair = pair ev.Time = tt0 - err = p.UpdatePNL(ev) + err = p.UpdatePNL(ev, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -784,7 +784,7 @@ func TestCalculatePNL(t *testing.T) { }, }, }, false) - err = p.UpdatePNL(ev) + err = p.UpdatePNL(ev, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index efb5aa4a774..ea6dc34870f 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -35,11 +35,11 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str } var relevantStats []relatedCurrencyPairStatistics for k, v := range exchangeAssetStats { - if k.Base == report.Items[i].Currency { + if k.Base.Equal(report.Items[i].Currency) { relevantStats = append(relevantStats, relatedCurrencyPairStatistics{isBaseCurrency: true, stat: v}) continue } - if k.Quote == report.Items[i].Currency { + if k.Quote.Equal(report.Items[i].Currency) { relevantStats = append(relevantStats, relatedCurrencyPairStatistics{stat: v}) } } diff --git a/backtester/eventhandlers/statistics/fundingstatistics_test.go b/backtester/eventhandlers/statistics/fundingstatistics_test.go index c517d79b5be..09efa162093 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics_test.go +++ b/backtester/eventhandlers/statistics/fundingstatistics_test.go @@ -10,6 +10,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) @@ -20,8 +21,11 @@ func TestCalculateFundingStatistics(t *testing.T) { if !errors.Is(err, common.ErrNilArguments) { t.Errorf("received %v expected %v", err, common.ErrNilArguments) } - f := funding.SetupFundingManager(true, true) - item, err := funding.CreateItem("binance", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero, false) + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, true, true) + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } + item, err := funding.CreateItem("binance", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } @@ -30,7 +34,7 @@ func TestCalculateFundingStatistics(t *testing.T) { t.Errorf("received %v expected %v", err, nil) } - item2, err := funding.CreateItem("binance", asset.Spot, currency.USD, decimal.NewFromInt(1337), decimal.Zero, false) + item2, err := funding.CreateItem("binance", asset.Spot, currency.USD, decimal.NewFromInt(1337), decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } @@ -76,7 +80,10 @@ func TestCalculateFundingStatistics(t *testing.T) { t.Errorf("received %v expected %v", err, errNoRelevantStatsFound) } - f = funding.SetupFundingManager(true, false) + f, err = funding.SetupFundingManager(&engine.ExchangeManager{}, true, false) + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } err = f.AddItem(item) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) @@ -183,12 +190,15 @@ func TestFundingStatisticsPrintResults(t *testing.T) { t.Errorf("received %v expected %v", err, common.ErrNilArguments) } - funds := funding.SetupFundingManager(true, true) - item1, err := funding.CreateItem("test", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04), false) + funds, err := funding.SetupFundingManager(&engine.ExchangeManager{}, true, true) + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } + item1, err := funding.CreateItem("test", asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04)) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } - item2, err := funding.CreateItem("test", asset.Spot, currency.LTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04), false) + item2, err := funding.CreateItem("test", asset.Spot, currency.LTC, decimal.NewFromInt(1337), decimal.NewFromFloat(0.04)) if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 7c2b9eedc0d..f8d5d1bceb3 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -16,6 +16,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -737,12 +738,15 @@ func TestCalculateTheResults(t *testing.T) { s.ExchangeAssetPairStatistics[exch][a][p2].Events[1].Holdings.QuoteInitialFunds = eleet s.ExchangeAssetPairStatistics[exch][a][p2].Events[1].Holdings.TotalValue = eleeet - funds := funding.SetupFundingManager(false, false) - pBase, err := funding.CreateItem(exch, a, p.Base, eleeet, decimal.Zero, false) + funds, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, false) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pQuote, err := funding.CreateItem(exch, a, p.Quote, eleeet, decimal.Zero, false) + pBase, err := funding.CreateItem(exch, a, p.Base, eleeet, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + pQuote, err := funding.CreateItem(exch, a, p.Quote, eleeet, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -755,11 +759,11 @@ func TestCalculateTheResults(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pBase2, err := funding.CreateItem(exch, a, p2.Base, eleeet, decimal.Zero, false) + pBase2, err := funding.CreateItem(exch, a, p2.Base, eleeet, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - pQuote2, err := funding.CreateItem(exch, a, p2.Quote, eleeet, decimal.Zero, false) + pQuote2, err := funding.CreateItem(exch, a, p2.Quote, eleeet, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -781,7 +785,10 @@ func TestCalculateTheResults(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, errMissingSnapshots) } - funds = funding.SetupFundingManager(false, true) + funds, err = funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } err = funds.AddPair(pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index c3ac4c2534b..73312b6f152 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -158,17 +158,17 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro for k, v := range customSettings { switch k { case openShortDistancePercentageString: - rsiHigh, ok := v.(float64) - if !ok || rsiHigh <= 0 { + osdp, ok := v.(float64) + if !ok || osdp <= 0 { return fmt.Errorf("%w provided rsi-high value could not be parsed: %v", base.ErrInvalidCustomSettings, v) } - s.openShortDistancePercentage = decimal.NewFromFloat(rsiHigh) + s.openShortDistancePercentage = decimal.NewFromFloat(osdp) case closeShortDistancePercentageString: - rsiLow, ok := v.(float64) - if !ok || rsiLow <= 0 { + csdp, ok := v.(float64) + if !ok || csdp <= 0 { return fmt.Errorf("%w provided rsi-low value could not be parsed: %v", base.ErrInvalidCustomSettings, v) } - s.closeShortDistancePercentage = decimal.NewFromFloat(rsiLow) + s.closeShortDistancePercentage = decimal.NewFromFloat(csdp) default: return fmt.Errorf("%w unrecognised custom setting key %v with value %v. Cannot apply", base.ErrInvalidCustomSettings, k, v) } @@ -179,6 +179,6 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro // SetDefaults not required for DCA func (s *Strategy) SetDefaults() { - s.closeShortDistancePercentage = decimal.NewFromInt(5) + s.openShortDistancePercentage = decimal.NewFromInt(5) s.closeShortDistancePercentage = decimal.NewFromInt(5) } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index c32dd79f57a..8743c5fcce7 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -13,7 +13,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" eventkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" @@ -30,8 +29,8 @@ func TestName(t *testing.T) { func TestSupportsSimultaneousProcessing(t *testing.T) { t.Parallel() s := Strategy{} - if s.SupportsSimultaneousProcessing() { - t.Error("expected false") + if !s.SupportsSimultaneousProcessing() { + t.Error("expected true") } } @@ -44,36 +43,28 @@ func TestSetCustomSettings(t *testing.T) { } float14 := float64(14) mappalopalous := make(map[string]interface{}) - mappalopalous[rsiPeriodKey] = float14 - mappalopalous[rsiLowKey] = float14 - mappalopalous[rsiHighKey] = float14 + mappalopalous[openShortDistancePercentageString] = float14 + mappalopalous[closeShortDistancePercentageString] = float14 err = s.SetCustomSettings(mappalopalous) if err != nil { t.Error(err) } - mappalopalous[rsiPeriodKey] = "14" + mappalopalous[openShortDistancePercentageString] = "14" err = s.SetCustomSettings(mappalopalous) if !errors.Is(err, base.ErrInvalidCustomSettings) { t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) } - mappalopalous[rsiPeriodKey] = float14 - mappalopalous[rsiLowKey] = "14" + mappalopalous[closeShortDistancePercentageString] = float14 + mappalopalous[openShortDistancePercentageString] = "14" err = s.SetCustomSettings(mappalopalous) if !errors.Is(err, base.ErrInvalidCustomSettings) { t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) } - mappalopalous[rsiLowKey] = float14 - mappalopalous[rsiHighKey] = "14" - err = s.SetCustomSettings(mappalopalous) - if !errors.Is(err, base.ErrInvalidCustomSettings) { - t.Errorf("received: %v, expected: %v", err, base.ErrInvalidCustomSettings) - } - - mappalopalous[rsiHighKey] = float14 + mappalopalous[closeShortDistancePercentageString] = float14 mappalopalous["lol"] = float14 err = s.SetCustomSettings(mappalopalous) if !errors.Is(err, base.ErrInvalidCustomSettings) { @@ -84,11 +75,11 @@ func TestSetCustomSettings(t *testing.T) { func TestOnSignal(t *testing.T) { t.Parallel() s := Strategy{ - rsiPeriod: decimal.NewFromInt(14), + openShortDistancePercentage: decimal.NewFromInt(14), } _, err := s.OnSignal(nil, nil, nil) - if !errors.Is(err, common.ErrNilEvent) { - t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) + if !errors.Is(err, base.ErrSimultaneousProcessingOnly) { + t.Errorf("received: %v, expected: %v", err, base.ErrSimultaneousProcessingOnly) } dStart := time.Date(2020, 1, 0, 0, 0, 0, 0, time.UTC) dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) @@ -119,15 +110,14 @@ func TestOnSignal(t *testing.T) { Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, } - var resp signal.Event _, err = s.OnSignal(da, nil, nil) - if !errors.Is(err, nil) { - t.Fatalf("expected: %v, received %v", nil, err) + if !errors.Is(err, base.ErrSimultaneousProcessingOnly) { + t.Fatalf("expected: %v, received %v", nil, base.ErrSimultaneousProcessingOnly) } - s.rsiPeriod = decimal.NewFromInt(1) - _, err = s.OnSignal(da, nil, nil) - if err != nil { - t.Error(err) + s.openShortDistancePercentage = decimal.NewFromInt(1) + _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) + if !errors.Is(err, errNotSetup) { + t.Fatalf("expected: %v, received %v", nil, errNotSetup) } da.Item = gctkline.Item{ @@ -157,12 +147,9 @@ func TestOnSignal(t *testing.T) { } da.RangeHolder = ranger da.RangeHolder.SetHasDataFromCandles(da.Item.Candles) - resp, err = s.OnSignal(da, nil, nil) - if err != nil { - t.Fatal(err) - } - if resp.GetDirection() != common.DoNothing { - t.Error("expected do nothing") + _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) + if !errors.Is(err, errNotSetup) { + t.Fatalf("expected: %v, received %v", nil, errNotSetup) } } @@ -170,8 +157,8 @@ func TestOnSignals(t *testing.T) { t.Parallel() s := Strategy{} _, err := s.OnSignal(nil, nil, nil) - if !errors.Is(err, common.ErrNilEvent) { - t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) + if !errors.Is(err, base.ErrSimultaneousProcessingOnly) { + t.Errorf("received: %v, expected: %v", err, base.ErrSimultaneousProcessingOnly) } dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) exch := "ftx" @@ -199,9 +186,9 @@ func TestOnSignals(t *testing.T) { RangeHolder: &gctkline.IntervalRangeHolder{}, } _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) - if !strings.Contains(err.Error(), base.ErrSimultaneousProcessingNotSupported.Error()) { + if !strings.Contains(err.Error(), errNotSetup.Error()) { // common.Errs type doesn't keep type - t.Errorf("received: %v, expected: %v", err, base.ErrSimultaneousProcessingNotSupported) + t.Errorf("received: %v, expected: %v", err, errNotSetup) } } @@ -209,13 +196,10 @@ func TestSetDefaults(t *testing.T) { t.Parallel() s := Strategy{} s.SetDefaults() - if !s.rsiHigh.Equal(decimal.NewFromInt(70)) { - t.Error("expected 70") - } - if !s.rsiLow.Equal(decimal.NewFromInt(30)) { - t.Error("expected 30") + if !s.openShortDistancePercentage.Equal(decimal.NewFromInt(5)) { + t.Errorf("expected 5, received %v", s.openShortDistancePercentage) } - if !s.rsiPeriod.Equal(decimal.NewFromInt(14)) { - t.Error("expected 14") + if !s.closeShortDistancePercentage.Equal(decimal.NewFromInt(5)) { + t.Errorf("expected 5, received %v", s.closeShortDistancePercentage) } } diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 47e4ed25111..310419f4c4f 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -108,6 +108,20 @@ func (c *Collateral) GetPairReleaser() (IPairReleaser, error) { return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } +func (c *Collateral) Reserve(amount decimal.Decimal, side order.Side) error { + switch side { + case order.Long, order.Short: + return c.Collateral.Reserve(amount) + default: + return fmt.Errorf("%w for %v %v %v. Unknown side %v", + errCannotAllocate, + c.Collateral.exchange, + c.Collateral.asset, + c.Collateral.currency, + side) + } +} + // GetCollateralReleaser func (c *Collateral) GetCollateralReleaser() (ICollateralReleaser, error) { return c, nil diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 24bafb93129..97ed4ba80eb 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -36,16 +36,20 @@ var ( errTransferMustBeSameCurrency = errors.New("cannot transfer to different currency") errCannotMatchTrackingToItem = errors.New("cannot match tracking data to funding items") errNotFutures = errors.New("item linking collateral currencies must be a futures asset") + errExchangeManagerRequired = errors.New("exchange manager required") ) // SetupFundingManager creates the funding holder. It carries knowledge about levels of funding // across all execution handlers and enables fund transfers -func SetupFundingManager(exchManager *engine.ExchangeManager, usingExchangeLevelFunding, disableUSDTracking bool) *FundManager { +func SetupFundingManager(exchManager *engine.ExchangeManager, usingExchangeLevelFunding, disableUSDTracking bool) (*FundManager, error) { + if exchManager == nil { + return nil, errExchangeManagerRequired + } return &FundManager{ usingExchangeLevelFunding: usingExchangeLevelFunding, disableUSDTracking: disableUSDTracking, exchangeManager: exchManager, - } + }, nil } // CreateFuturesCurrencyCode converts a currency pair into a code @@ -84,7 +88,7 @@ func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) err return errNotFutures } for i := range f.items { - if f.items[i].currency.Match(code) && f.items[i].asset == item.asset { + if f.items[i].currency.Equal(code) && f.items[i].asset == item.asset { item.pairedWith = f.items[i] return nil } @@ -158,7 +162,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { } if strings.EqualFold(f.items[i].exchange, k.Item.Exchange) && f.items[i].asset == k.Item.Asset { - if f.items[i].currency == k.Item.Pair.Base { + if f.items[i].currency.Equal(k.Item.Pair.Base) { if f.items[i].usdTrackingCandles == nil && trackingcurrencies.CurrencyIsUSDTracked(k.Item.Pair.Quote) { f.items[i].usdTrackingCandles = k @@ -169,7 +173,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { baseSet = true } if trackingcurrencies.CurrencyIsUSDTracked(f.items[i].currency) { - if f.items[i].pairedWith != nil && f.items[i].currency != basePairedWith { + if f.items[i].pairedWith != nil && !f.items[i].currency.Equal(basePairedWith) { continue } if f.items[i].usdTrackingCandles == nil { @@ -313,10 +317,10 @@ func (f *FundManager) Transfer(amount decimal.Decimal, sender, receiver *Item, i } } - if sender.currency != receiver.currency { + if !sender.currency.Equal(receiver.currency) { return errTransferMustBeSameCurrency } - if sender.currency == receiver.currency && + if sender.currency.Equal(receiver.currency) && sender.exchange == receiver.exchange && sender.asset == receiver.asset { return fmt.Errorf("%v %v %v %w", sender.exchange, sender.asset, sender.currency, errCannotTransferToSameFunds) @@ -417,7 +421,7 @@ func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pai // GetFundingForEAC This will construct a funding based on the exchange, asset, currency code func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Code) (*Item, error) { for i := range f.items { - if f.items[i].BasicEqual(exch, a, c, currency.Code{}) { + if f.items[i].BasicEqual(exch, a, c, currency.EMPTYCODE) { return f.items[i], nil } } @@ -506,13 +510,13 @@ func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, pair cu CollateralCurrency: f.items[i].currency, Asset: f.items[i].asset, Side: side, - CollateralAmount: f.items[i].available, - CollateralPrice: usd, + FreeCollateral: f.items[i].available, + USDPrice: usd, }) if err != nil { return err } - collateralAmount = collateralAmount.Add(latest) + collateralAmount = latest.AvailableForUseAsCollateral } collat, err := exchMap[exchName].GetCollateralCurrencyForContract(item, pair) @@ -523,7 +527,7 @@ func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, pair cu for i := range f.items { if f.items[i].exchange == exchName && f.items[i].asset == item && - f.items[i].currency.Match(collat) { + f.items[i].currency.Equal(collat) { f.items[i].available = collateralAmount return nil } diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 79a60bba8ed..c7d353f0bbb 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -9,6 +9,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -27,14 +28,20 @@ var ( func TestSetupFundingManager(t *testing.T) { t.Parallel() - f := SetupFundingManager(true, false) + f, err := SetupFundingManager(&engine.ExchangeManager{}, true, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if !f.usingExchangeLevelFunding { t.Errorf("expected '%v received '%v'", true, false) } if f.disableUSDTracking { t.Errorf("expected '%v received '%v'", false, true) } - f = SetupFundingManager(false, true) + f, err = SetupFundingManager(&engine.ExchangeManager{}, false, true) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if f.usingExchangeLevelFunding { t.Errorf("expected '%v received '%v'", false, true) } @@ -45,7 +52,10 @@ func TestSetupFundingManager(t *testing.T) { func TestReset(t *testing.T) { t.Parallel() - f := SetupFundingManager(true, false) + f, err := SetupFundingManager(&engine.ExchangeManager{}, true, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) @@ -65,7 +75,10 @@ func TestReset(t *testing.T) { func TestIsUsingExchangeLevelFunding(t *testing.T) { t.Parallel() - f := SetupFundingManager(true, false) + f, err := SetupFundingManager(&engine.ExchangeManager{}, true, false) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if !f.IsUsingExchangeLevelFunding() { t.Errorf("expected '%v received '%v'", true, false) } @@ -263,7 +276,6 @@ func TestAddPair(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - p, err = CreatePair(baseItem, quoteItem) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) @@ -753,10 +765,10 @@ func TestGenerateReport(t *testing.T) { t.Parallel() f := FundManager{} report := f.GenerateReport() - if report == nil { + if report == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("shouldn't be nil") } - if len(report.Items) > 0 { + if len(report.Items) > 0 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Error("expected 0") } item := &Item{ @@ -837,7 +849,7 @@ func TestMatchesCurrency(t *testing.T) { if !i.MatchesCurrency(currency.BTC) { t.Error("expected true") } - if i.MatchesCurrency(currency.Code{}) { + if i.MatchesCurrency(currency.EMPTYCODE) { t.Error("expected false") } if i.MatchesCurrency(currency.NewCode("")) { @@ -851,7 +863,7 @@ func TestCreateSnapshot(t *testing.T) { f.items = append(f.items, &Item{ exchange: "", asset: "", - currency: currency.Code{}, + currency: currency.EMPTYCODE, initialFunds: decimal.Decimal{}, available: decimal.Decimal{}, reserved: decimal.Decimal{}, diff --git a/backtester/funding/item.go b/backtester/funding/item.go index e02b183cbbf..0fc1aa81307 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -97,7 +97,7 @@ func (i *Item) BasicEqual(exch string, a asset.Item, currency, pairedCurrency cu i.asset == a && i.currency == currency && (i.pairedWith == nil || - (i.pairedWith != nil && i.pairedWith.currency.Match(pairedCurrency))) + (i.pairedWith != nil && i.pairedWith.currency.Equal(pairedCurrency))) } // MatchesCurrency checks that an item's currency is equal diff --git a/backtester/funding/trackingcurrencies/trackingcurrencies.go b/backtester/funding/trackingcurrencies/trackingcurrencies.go index 121a7b2f38e..4192d61c18f 100644 --- a/backtester/funding/trackingcurrencies/trackingcurrencies.go +++ b/backtester/funding/trackingcurrencies/trackingcurrencies.go @@ -125,13 +125,13 @@ func pairContainsUSD(pair currency.Pair) bool { // this will allow for data retrieval and total tracking on backtesting runs func findMatchingUSDPairs(pair currency.Pair, pairs *currency.PairStore) (basePair, quotePair currency.Pair, err error) { if pairs == nil { - return currency.Pair{}, currency.Pair{}, errNilPairs + return currency.EMPTYPAIR, currency.EMPTYPAIR, errNilPairs } if pairContainsUSD(pair) { - return currency.Pair{}, currency.Pair{}, ErrCurrencyContainsUSD + return currency.EMPTYPAIR, currency.EMPTYPAIR, ErrCurrencyContainsUSD } if !pairs.Available.Contains(pair, true) { - return currency.Pair{}, currency.Pair{}, fmt.Errorf("%v %w", pair, errCurrencyNotFoundInPairs) + return currency.EMPTYPAIR, currency.EMPTYPAIR, fmt.Errorf("%v %w", pair, errCurrencyNotFoundInPairs) } var baseFound, quoteFound bool diff --git a/backtester/funding/trackingcurrencies/trackingcurrencies_test.go b/backtester/funding/trackingcurrencies/trackingcurrencies_test.go index 7c3ee386a84..72447f390e7 100644 --- a/backtester/funding/trackingcurrencies/trackingcurrencies_test.go +++ b/backtester/funding/trackingcurrencies/trackingcurrencies_test.go @@ -82,8 +82,8 @@ func TestFindMatchingUSDPairs(t *testing.T) { description: "already has USD", initialPair: currency.NewPair(currency.BTC, currency.USDT), availablePairs: ¤cy.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.USDT)}}, - basePair: currency.Pair{}, - quotePair: currency.Pair{}, + basePair: currency.EMPTYPAIR, + quotePair: currency.EMPTYPAIR, expectedErr: ErrCurrencyContainsUSD, }, { @@ -99,14 +99,14 @@ func TestFindMatchingUSDPairs(t *testing.T) { initialPair: currency.NewPair(currency.BTC, currency.LTC), availablePairs: ¤cy.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.LTC), currency.NewPair(currency.BTC, currency.DAI)}}, basePair: currency.NewPair(currency.BTC, currency.DAI), - quotePair: currency.Pair{}, + quotePair: currency.EMPTYPAIR, expectedErr: errNoMatchingQuoteUSDFound, }, { description: "base currency has no matching USD pair", initialPair: currency.NewPair(currency.BTC, currency.LTC), availablePairs: ¤cy.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.LTC), currency.NewPair(currency.LTC, currency.USDT)}}, - basePair: currency.Pair{}, + basePair: currency.EMPTYPAIR, quotePair: currency.NewPair(currency.LTC, currency.USDT), expectedErr: errNoMatchingBaseUSDFound, }, @@ -114,16 +114,16 @@ func TestFindMatchingUSDPairs(t *testing.T) { description: "both base and quote don't have USD pairs", initialPair: currency.NewPair(currency.BTC, currency.LTC), availablePairs: ¤cy.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.LTC)}}, - basePair: currency.Pair{}, - quotePair: currency.Pair{}, + basePair: currency.EMPTYPAIR, + quotePair: currency.EMPTYPAIR, expectedErr: errNoMatchingPairUSDFound, }, { description: "currency doesnt exist in available pairs", initialPair: currency.NewPair(currency.BTC, currency.LTC), availablePairs: ¤cy.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.DOGE)}}, - basePair: currency.Pair{}, - quotePair: currency.Pair{}, + basePair: currency.EMPTYPAIR, + quotePair: currency.EMPTYPAIR, expectedErr: errCurrencyNotFoundInPairs, }, } diff --git a/cmd/apichecker/apicheck.go b/cmd/apichecker/apicheck.go index 8d6850191ef..f34dccf2051 100644 --- a/cmd/apichecker/apicheck.go +++ b/cmd/apichecker/apicheck.go @@ -1290,15 +1290,19 @@ func updateFile(name string) error { // SendGetReq sends get req func sendGetReq(path string, result interface{}) error { var requester *request.Requester + var err error if strings.Contains(path, "github") { - requester = request.New("Apichecker", + requester, err = request.New("Apichecker", common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(time.Hour, 60))) } else { - requester = request.New("Apichecker", + requester, err = request.New("Apichecker", common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(time.Second, 100))) } + if err != nil { + return err + } item := &request.Item{ Method: http.MethodGet, Path: path, @@ -1311,9 +1315,12 @@ func sendGetReq(path string, result interface{}) error { // sendAuthReq sends auth req func sendAuthReq(method, path string, result interface{}) error { - requester := request.New("Apichecker", + requester, err := request.New("Apichecker", common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(time.Second*10, 100))) + if err != nil { + return err + } item := &request.Item{ Method: method, Path: path, diff --git a/cmd/documentation/currency_templates/fx.tmpl b/cmd/documentation/currency_templates/fx.tmpl index 964f9786d53..14a0fba971c 100644 --- a/cmd/documentation/currency_templates/fx.tmpl +++ b/cmd/documentation/currency_templates/fx.tmpl @@ -4,6 +4,7 @@ + Currency Converter API support + Currency Layer support ++ Exchange Rates support + Fixer.io support + Open Exchange Rates support + ExchangeRate.host support diff --git a/cmd/documentation/root_templates/root_readme.tmpl b/cmd/documentation/root_templates/root_readme.tmpl index 6cce76df29d..8a1b47e28b6 100644 --- a/cmd/documentation/root_templates/root_readme.tmpl +++ b/cmd/documentation/root_templates/root_readme.tmpl @@ -69,7 +69,7 @@ However, we welcome pull requests for any exchange which does not match this cri + Connection monitor package. + gRPC service and JSON RPC proxy. See [gRPC service](/gctrpc/README.md). + gRPC client. See [gctcli](/cmd/gctcli/README.md). -+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Fixer.io, OpenExchangeRates). ++ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Exchange Rates, Fixer.io, OpenExchangeRates, Exchange Rate Host). + Packages for handling currency pairs, tickers and orderbooks. + Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking. + Basic event trigger system. diff --git a/cmd/exchange_wrapper_issues/exchange_wrapper_issues_test.go b/cmd/exchange_wrapper_issues/exchange_wrapper_issues_test.go index 5fd81bb096e..fbbbd1a1cbd 100644 --- a/cmd/exchange_wrapper_issues/exchange_wrapper_issues_test.go +++ b/cmd/exchange_wrapper_issues/exchange_wrapper_issues_test.go @@ -7,7 +7,7 @@ import ( ) func TestDisruptFormatting(t *testing.T) { - _, err := disruptFormatting(currency.Pair{}) + _, err := disruptFormatting(currency.EMPTYPAIR) if err == nil { t.Fatal("error cannot be nil") } diff --git a/cmd/exchange_wrapper_issues/main.go b/cmd/exchange_wrapper_issues/main.go index 122b346f3c7..dbbcd9a4087 100644 --- a/cmd/exchange_wrapper_issues/main.go +++ b/cmd/exchange_wrapper_issues/main.go @@ -953,10 +953,10 @@ func outputToConsole(exchangeResponses []ExchangeResponses) { // ensure format currency pair is used throughout the code base. func disruptFormatting(p currency.Pair) (currency.Pair, error) { if p.Base.IsEmpty() { - return currency.Pair{}, errors.New("cannot disrupt formatting as base is not populated") + return currency.EMPTYPAIR, errors.New("cannot disrupt formatting as base is not populated") } if p.Quote.IsEmpty() { - return currency.Pair{}, errors.New("cannot disrupt formatting as quote is not populated") + return currency.EMPTYPAIR, errors.New("cannot disrupt formatting as quote is not populated") } return currency.Pair{ diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 47b4fd7f073..a96e1123223 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -4860,7 +4860,7 @@ func getFuturesPositions(c *cli.Context) error { var verbose bool if c.IsSet("verbose") { verbose = c.Bool("verbose") - } else if c.Args().Get(6) != "" { + } else if c.Args().Get(7) != "" { verbose, err = strconv.ParseBool(c.Args().Get(7)) if err != nil { return err @@ -4870,8 +4870,8 @@ func getFuturesPositions(c *cli.Context) error { var overwrite bool if c.IsSet("overwrite") { overwrite = c.Bool("overwrite") - } else if c.Args().Get(2) != "" { - overwrite, err = strconv.ParseBool(c.Args().Get(2)) + } else if c.Args().Get(8) != "" { + overwrite, err = strconv.ParseBool(c.Args().Get(8)) if err != nil { return err } @@ -5005,23 +5005,23 @@ func getCollateral(c *cli.Context) error { } } - var subAccount string - if c.IsSet("subaccount") { - subAccount = c.String("subaccount") - } else if c.Args().Get(4) != "" { - subAccount = c.Args().Get(4) - } - var includeZeroValues bool if c.IsSet("includezerovalues") { includeZeroValues = c.Bool("includezerovalues") - } else if c.Args().Get(5) != "" { - includeZeroValues, err = strconv.ParseBool(c.Args().Get(5)) + } else if c.Args().Get(4) != "" { + includeZeroValues, err = strconv.ParseBool(c.Args().Get(4)) if err != nil { return err } } + var subAccount string + if c.IsSet("subaccount") { + subAccount = c.String("subaccount") + } else if c.Args().Get(5) != "" { + subAccount = c.Args().Get(5) + } + conn, cancel, err := setupClient(c) if err != nil { return err diff --git a/cmd/portfolio/portfolio.go b/cmd/portfolio/portfolio.go index f6536a38a29..5b187530b45 100644 --- a/cmd/portfolio/portfolio.go +++ b/cmd/portfolio/portfolio.go @@ -9,13 +9,12 @@ import ( "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/bitfinex" "github.com/thrasher-corp/gocryptotrader/portfolio" ) var ( - priceMap map[currency.Code]float64 + priceMap map[*currency.Item]float64 displayCurrency currency.Code ) @@ -23,10 +22,8 @@ func printSummary(msg string, amount float64) { log.Println() log.Println(fmt.Sprintf("%s in USD: $%.2f", msg, amount)) - if displayCurrency != currency.USD { - conv, err := currency.ConvertCurrency(amount, - currency.USD, - displayCurrency) + if !displayCurrency.Equal(currency.USD) { + conv, err := currency.ConvertFiat(amount, currency.USD, displayCurrency) if err != nil { log.Println(err) } else { @@ -50,7 +47,7 @@ func printSummary(msg string, amount float64) { func getOnlineOfflinePortfolio(coins []portfolio.Coin, online bool) { var totals float64 for _, x := range coins { - value := priceMap[x.Coin] * x.Balance + value := priceMap[x.Coin.Item] * x.Balance totals += value log.Printf("\t%v %v Subtotal: $%.2f Coin percentage: %.2f%%\n", x.Coin, x.Balance, value, x.Percentage) @@ -90,13 +87,7 @@ func main() { Subtotal float64 } - err = cfg.RetrieveConfigCurrencyPairs(true, asset.Spot) - if err != nil { - log.Printf("Failed to retrieve config currency pairs %v\n", err) - os.Exit(1) - } - - portfolioMap := make(map[currency.Code]PortfolioTemp) + portfolioMap := make(map[*currency.Item]PortfolioTemp) total := float64(0) log.Println("Fetching currency data..") @@ -114,21 +105,21 @@ func main() { log.Println("Fetched currency data.") log.Println("Fetching ticker data and calculating totals..") - priceMap = make(map[currency.Code]float64) - priceMap[currency.USD] = 1 + priceMap = make(map[*currency.Item]float64) + priceMap[currency.USD.Item] = 1 for _, y := range result.Totals { pf := PortfolioTemp{} pf.Balance = y.Balance pf.Subtotal = 0 - if y.Coin.IsDefaultFiatCurrency() { - if y.Coin != currency.USD { - conv, err := currency.ConvertCurrency(y.Balance, y.Coin, currency.USD) + if y.Coin.IsFiatCurrency() { + if !y.Coin.Equal(currency.USD) { + conv, err := currency.ConvertFiat(y.Balance, y.Coin, currency.USD) if err != nil { log.Println(err) } else { - priceMap[y.Coin] = conv / y.Balance + priceMap[y.Coin.Item] = conv / y.Balance pf.Subtotal = conv } } else { @@ -143,18 +134,25 @@ func main() { if errf != nil { log.Println(errf) } else { - priceMap[y.Coin] = ticker.Last + priceMap[y.Coin.Item] = ticker.Last pf.Subtotal = ticker.Last * y.Balance } } - portfolioMap[y.Coin] = pf + portfolioMap[y.Coin.Item] = pf total += pf.Subtotal } log.Println("Done.") log.Println() log.Println("PORTFOLIO TOTALS:") for x, y := range portfolioMap { - log.Printf("\t%s Amount: %f Subtotal: $%.2f USD (1 %s = $%.2f USD). Percentage of portfolio %.3f%%", x, y.Balance, y.Subtotal, x, y.Subtotal/y.Balance, y.Subtotal/total*100/1) + code := currency.Code{Item: x} + log.Printf("\t%s Amount: %f Subtotal: $%.2f USD (1 %s = $%.2f USD). Percentage of portfolio %.3f%%", + code, + y.Balance, + y.Subtotal, + code, + y.Subtotal/y.Balance, + y.Subtotal/total*100/1) } printSummary("\tTotal balance", total) @@ -170,7 +168,7 @@ func main() { log.Printf("\t%s:", x) totals = 0 for z := range y { - value := priceMap[x] * y[z].Balance + value := priceMap[x.Item] * y[z].Balance totals += value log.Printf("\t %s Amount: %f Subtotal: $%.2f Coin percentage: %.2f%%\n", y[z].Address, y[z].Balance, value, y[z].Percentage) @@ -183,7 +181,7 @@ func main() { log.Printf("\t%s:", x) totals = 0 for z, w := range y { - value := priceMap[z] * w.Balance + value := priceMap[z.Item] * w.Balance totals += value log.Printf("\t %s Amount: %f Subtotal $%.2f Coin percentage: %.2f%%", z, w.Balance, value, w.Percentage) diff --git a/cmd/portfolio/portfolio_test.go b/cmd/portfolio/portfolio_test.go deleted file mode 100644 index e11a9212d04..00000000000 --- a/cmd/portfolio/portfolio_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "testing" - -func TestMain(t *testing.T) { - -} diff --git a/config/config.go b/config/config.go index 6ce2f10c259..1c3035dd8c6 100644 --- a/config/config.go +++ b/config/config.go @@ -33,7 +33,7 @@ import ( var errExchangeConfigIsNil = errors.New("exchange config is nil") // GetCurrencyConfig returns currency configurations -func (c *Config) GetCurrencyConfig() CurrencyConfig { +func (c *Config) GetCurrencyConfig() currency.Config { return c.Currency } @@ -191,7 +191,7 @@ func (c *Config) UpdateCommunicationsConfig(config *base.CommunicationsConfig) { } // GetCryptocurrencyProviderConfig returns the communications configuration -func (c *Config) GetCryptocurrencyProviderConfig() CryptocurrencyProvider { +func (c *Config) GetCryptocurrencyProviderConfig() currency.Provider { m.Lock() provider := c.Currency.CryptocurrencyProvider m.Unlock() @@ -199,7 +199,7 @@ func (c *Config) GetCryptocurrencyProviderConfig() CryptocurrencyProvider { } // UpdateCryptocurrencyProviderConfig returns the communications configuration -func (c *Config) UpdateCryptocurrencyProviderConfig(config CryptocurrencyProvider) { +func (c *Config) UpdateCryptocurrencyProviderConfig(config currency.Provider) { m.Lock() c.Currency.CryptocurrencyProvider = config m.Unlock() @@ -732,7 +732,7 @@ func (c *Config) CountEnabledExchanges() int { } // GetCurrencyPairDisplayConfig retrieves the currency pair display preference -func (c *Config) GetCurrencyPairDisplayConfig() *CurrencyPairFormatConfig { +func (c *Config) GetCurrencyPairDisplayConfig() *currency.PairFormat { return c.Currency.CurrencyPairFormat } @@ -756,38 +756,6 @@ func (c *Config) GetExchangeConfig(name string) (*Exchange, error) { return nil, fmt.Errorf("%s %w", name, ErrExchangeNotFound) } -// GetForexProvider returns a forex provider configuration by its name -func (c *Config) GetForexProvider(name string) (currency.FXSettings, error) { - m.Lock() - defer m.Unlock() - for i := range c.Currency.ForexProviders { - if strings.EqualFold(c.Currency.ForexProviders[i].Name, name) { - return c.Currency.ForexProviders[i], nil - } - } - return currency.FXSettings{}, errors.New("provider not found") -} - -// GetForexProviders returns a list of available forex providers -func (c *Config) GetForexProviders() []currency.FXSettings { - m.Lock() - fxProviders := c.Currency.ForexProviders - m.Unlock() - return fxProviders -} - -// GetPrimaryForexProvider returns the primary forex provider -func (c *Config) GetPrimaryForexProvider() string { - m.Lock() - defer m.Unlock() - for i := range c.Currency.ForexProviders { - if c.Currency.ForexProviders[i].PrimaryProvider { - return c.Currency.ForexProviders[i].Name - } - } - return "" -} - // UpdateExchangeConfig updates exchange configurations func (c *Config) UpdateExchangeConfig(e *Exchange) error { m.Lock() @@ -903,13 +871,19 @@ func (c *Config) CheckExchangeConfigValues() error { c.Exchanges[i].EnabledPairs = nil } else { assets := c.Exchanges[i].CurrencyPairs.GetAssetTypes(false) + if len(assets) == 0 { + c.Exchanges[i].Enabled = false + log.Warnf(log.ConfigMgr, "%s no assets found, disabling...", c.Exchanges[i].Name) + continue + } + var atLeastOne bool for index := range assets { err := c.Exchanges[i].CurrencyPairs.IsAssetEnabled(assets[index]) if err != nil { - // Checks if we have an old config without the ability to - // enable disable the entire asset - if err.Error() == "cannot ascertain if asset is enabled, variable is nil" { + if errors.Is(err, currency.ErrAssetIsNil) { + // Checks if we have an old config without the ability to + // enable disable the entire asset log.Warnf(log.ConfigMgr, "Exchange %s: upgrading config for asset type %s and setting enabled.\n", c.Exchanges[i].Name, @@ -926,14 +900,6 @@ func (c *Config) CheckExchangeConfigValues() error { } if !atLeastOne { - if len(assets) == 0 { - c.Exchanges[i].Enabled = false - log.Warnf(log.ConfigMgr, - "%s no assets found, disabling...", - c.Exchanges[i].Name) - continue - } - // turn on an asset if all disabled log.Warnf(log.ConfigMgr, "%s assets disabled, turning on asset %s", @@ -1076,100 +1042,74 @@ func (c *Config) CheckBankAccountConfig() { banking.SetAccounts(c.BankAccounts...) } -// CheckCurrencyConfigValues checks to see if the currency config values are correct or not -func (c *Config) CheckCurrencyConfigValues() error { - fxProviders := forexprovider.GetSupportedForexProviders() +// GetForexProviders returns a list of available forex providers +func (c *Config) GetForexProviders() []currency.FXSettings { + m.Lock() + fxProviders := c.Currency.ForexProviders + m.Unlock() + return fxProviders +} - if len(fxProviders) != len(c.Currency.ForexProviders) { - for x := range fxProviders { - _, err := c.GetForexProvider(fxProviders[x]) - if err != nil { - log.Warnf(log.Global, "%s forex provider not found, adding to config..\n", fxProviders[x]) - c.Currency.ForexProviders = append(c.Currency.ForexProviders, currency.FXSettings{ - Name: fxProviders[x], - RESTPollingDelay: 600, - APIKey: DefaultUnsetAPIKey, - APIKeyLvl: -1, - }) - } +// GetPrimaryForexProvider returns the primary forex provider +func (c *Config) GetPrimaryForexProvider() string { + m.Lock() + defer m.Unlock() + for i := range c.Currency.ForexProviders { + if c.Currency.ForexProviders[i].PrimaryProvider { + return c.Currency.ForexProviders[i].Name } } + return "" +} - count := 0 +// forexProviderExists checks to see if the provider exist. +func (c *Config) forexProviderExists(name string) bool { for i := range c.Currency.ForexProviders { - if c.Currency.ForexProviders[i].Enabled { - if (c.Currency.ForexProviders[i].Name == "CurrencyConverter" || c.Currency.ForexProviders[i].Name == "ExchangeRates") && - c.Currency.ForexProviders[i].PrimaryProvider && - (c.Currency.ForexProviders[i].APIKey == "" || - c.Currency.ForexProviders[i].APIKey == DefaultUnsetAPIKey) { - log.Warnf(log.Global, "%s forex provider no longer supports unset API key requests. Switching to %s FX provider..", - c.Currency.ForexProviders[i].Name, DefaultForexProviderExchangeRatesAPI) - c.Currency.ForexProviders[i].Enabled = false - c.Currency.ForexProviders[i].PrimaryProvider = false - c.Currency.ForexProviders[i].APIKey = DefaultUnsetAPIKey - c.Currency.ForexProviders[i].APIKeyLvl = -1 - continue - } - if c.Currency.ForexProviders[i].APIKey == DefaultUnsetAPIKey && - c.Currency.ForexProviders[i].Name != DefaultForexProviderExchangeRatesAPI { - log.Warnf(log.Global, "%s enabled forex provider API key not set. Please set this in your config.json file\n", c.Currency.ForexProviders[i].Name) - c.Currency.ForexProviders[i].Enabled = false - c.Currency.ForexProviders[i].PrimaryProvider = false - continue - } + if strings.EqualFold(c.Currency.ForexProviders[i].Name, name) { + return true + } + } + return false +} - if c.Currency.ForexProviders[i].APIKeyLvl == -1 && c.Currency.ForexProviders[i].Name != DefaultForexProviderExchangeRatesAPI { - log.Warnf(log.Global, "%s APIKey Level not set, functions limited. Please set this in your config.json file\n", - c.Currency.ForexProviders[i].Name) - } - count++ +// CheckCurrencyConfigValues checks to see if the currency config values are +// correct or not +func (c *Config) CheckCurrencyConfigValues() error { + supported := forexprovider.GetSupportedForexProviders() + for x := range supported { + if !c.forexProviderExists(supported[x]) { + log.Warnf(log.ConfigMgr, "%s forex provider not found, adding to config...\n", supported[x]) + c.Currency.ForexProviders = append(c.Currency.ForexProviders, + currency.FXSettings{ + Name: supported[x], + APIKey: DefaultUnsetAPIKey, + APIKeyLvl: -1, + }) } } - if count == 0 { - for x := range c.Currency.ForexProviders { - if c.Currency.ForexProviders[x].Name == DefaultForexProviderExchangeRatesAPI { - c.Currency.ForexProviders[x].Enabled = true - c.Currency.ForexProviders[x].PrimaryProvider = true - log.Warnf(log.ConfigMgr, "No valid forex providers configured. Defaulting to %s.", - DefaultForexProviderExchangeRatesAPI) - } + for i := range c.Currency.ForexProviders { + if !common.StringDataContainsInsensitive(supported, c.Currency.ForexProviders[i].Name) { + log.Warnf(log.ConfigMgr, + "%s forex provider not supported, please remove from config.\n", + c.Currency.ForexProviders[i].Name) + c.Currency.ForexProviders[i].Enabled = false } } - if c.Currency.CryptocurrencyProvider == (CryptocurrencyProvider{}) { + if c.Currency.CryptocurrencyProvider == (currency.Provider{}) { c.Currency.CryptocurrencyProvider.Name = "CoinMarketCap" c.Currency.CryptocurrencyProvider.Enabled = false c.Currency.CryptocurrencyProvider.Verbose = false c.Currency.CryptocurrencyProvider.AccountPlan = DefaultUnsetAccountPlan - c.Currency.CryptocurrencyProvider.APIkey = DefaultUnsetAPIKey + c.Currency.CryptocurrencyProvider.APIKey = DefaultUnsetAPIKey } - if c.Currency.CryptocurrencyProvider.Enabled { - if c.Currency.CryptocurrencyProvider.APIkey == "" || - c.Currency.CryptocurrencyProvider.APIkey == DefaultUnsetAPIKey { - log.Warnln(log.ConfigMgr, "CryptocurrencyProvider enabled but api key is unset please set this in your config.json file") - } - if c.Currency.CryptocurrencyProvider.AccountPlan == "" || - c.Currency.CryptocurrencyProvider.AccountPlan == DefaultUnsetAccountPlan { - log.Warnln(log.ConfigMgr, "CryptocurrencyProvider enabled but account plan is unset please set this in your config.json file") - } - } else { - if c.Currency.CryptocurrencyProvider.APIkey == "" { - c.Currency.CryptocurrencyProvider.APIkey = DefaultUnsetAPIKey - } - if c.Currency.CryptocurrencyProvider.AccountPlan == "" { - c.Currency.CryptocurrencyProvider.AccountPlan = DefaultUnsetAccountPlan - } + if c.Currency.CryptocurrencyProvider.APIKey == "" { + c.Currency.CryptocurrencyProvider.APIKey = DefaultUnsetAPIKey } - - if c.Currency.Cryptocurrencies.Join() == "" { - if c.Cryptocurrencies != nil { - c.Currency.Cryptocurrencies = *c.Cryptocurrencies - c.Cryptocurrencies = nil - } else { - c.Currency.Cryptocurrencies = currency.GetDefaultCryptocurrencies() - } + if c.Currency.CryptocurrencyProvider.AccountPlan == "" { + c.Currency.CryptocurrencyProvider.AccountPlan = DefaultUnsetAccountPlan } if c.Currency.CurrencyPairFormat == nil { @@ -1177,7 +1117,7 @@ func (c *Config) CheckCurrencyConfigValues() error { c.Currency.CurrencyPairFormat = c.CurrencyPairFormat c.CurrencyPairFormat = nil } else { - c.Currency.CurrencyPairFormat = &CurrencyPairFormatConfig{ + c.Currency.CurrencyPairFormat = ¤cy.PairFormat{ Delimiter: "-", Uppercase: true, } @@ -1198,65 +1138,16 @@ func (c *Config) CheckCurrencyConfigValues() error { c.FiatDisplayCurrency = nil } - return nil -} - -// RetrieveConfigCurrencyPairs splits, assigns and verifies enabled currency -// pairs either cryptoCurrencies or fiatCurrencies -func (c *Config) RetrieveConfigCurrencyPairs(enabledOnly bool, assetType asset.Item) error { - cryptoCurrencies := c.Currency.Cryptocurrencies - fiatCurrencies := currency.GetFiatCurrencies() - - for x := range c.Exchanges { - if !c.Exchanges[x].Enabled && enabledOnly { - continue - } - - err := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType) - if err != nil { - continue - } - - baseCurrencies := c.Exchanges[x].BaseCurrencies - for y := range baseCurrencies { - if !fiatCurrencies.Contains(baseCurrencies[y]) { - fiatCurrencies = append(fiatCurrencies, baseCurrencies[y]) - } - } + if c.Currency.CurrencyFileUpdateDuration <= 0 { + log.Warnf(log.ConfigMgr, "Currency file update duration invalid, defaulting to %s", currency.DefaultCurrencyFileDelay) + c.Currency.CurrencyFileUpdateDuration = currency.DefaultCurrencyFileDelay } - for x := range c.Exchanges { - err := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType) - if err != nil { - continue - } - - var pairs []currency.Pair - if !c.Exchanges[x].Enabled && enabledOnly { - pairs, err = c.GetEnabledPairs(c.Exchanges[x].Name, assetType) - } else { - pairs, err = c.GetAvailablePairs(c.Exchanges[x].Name, assetType) - } - - if err != nil { - return err - } - - for y := range pairs { - if !fiatCurrencies.Contains(pairs[y].Base) && - !cryptoCurrencies.Contains(pairs[y].Base) { - cryptoCurrencies = append(cryptoCurrencies, pairs[y].Base) - } - - if !fiatCurrencies.Contains(pairs[y].Quote) && - !cryptoCurrencies.Contains(pairs[y].Quote) { - cryptoCurrencies = append(cryptoCurrencies, pairs[y].Quote) - } - } + if c.Currency.ForeignExchangeUpdateDuration <= 0 { + log.Warnf(log.ConfigMgr, "Currency foreign exchange update duration invalid, defaulting to %s", currency.DefaultForeignExchangeDelay) + c.Currency.ForeignExchangeUpdateDuration = currency.DefaultForeignExchangeDelay } - currency.UpdateCurrencies(fiatCurrencies, false) - currency.UpdateCurrencies(cryptoCurrencies, true) return nil } @@ -1282,7 +1173,7 @@ func (c *Config) CheckLoggerConfig() error { c.Logging.LoggerFileConfig.Rotate = convert.BoolPtr(false) } if c.Logging.LoggerFileConfig.MaxSize <= 0 { - log.Warnf(log.Global, "Logger rotation size invalid, defaulting to %v", log.DefaultMaxFileSize) + log.Warnf(log.ConfigMgr, "Logger rotation size invalid, defaulting to %v", log.DefaultMaxFileSize) c.Logging.LoggerFileConfig.MaxSize = log.DefaultMaxFileSize } log.FileLoggingConfiguredCorrectly = true @@ -1676,7 +1567,7 @@ func (c *Config) SaveConfigToFile(configPath string) error { if writer != nil { err = writer.Close() if err != nil { - log.Error(log.Global, err) + log.Error(log.ConfigMgr, err) } } }() @@ -1786,7 +1677,7 @@ func (c *Config) CheckConfig() error { err = c.checkGCTScriptConfig() if err != nil { - log.Errorf(log.Global, + log.Errorf(log.ConfigMgr, "Failed to configure gctscript, feature has been disabled: %s\n", err) } diff --git a/config/config_test.go b/config/config_test.go index 52e71002be3..a3ac049e66f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -46,7 +46,7 @@ func TestGetNonExistentDefaultFilePathDoesNotCreateDefaultDir(t *testing.T) { func TestGetCurrencyConfig(t *testing.T) { t.Parallel() cfg := &Config{ - Currency: CurrencyConfig{ + Currency: currency.Config{ ForeignExchangeUpdateDuration: time.Second, }, } @@ -361,8 +361,8 @@ func TestUpdateCommunicationsConfig(t *testing.T) { func TestGetCryptocurrencyProviderConfig(t *testing.T) { t.Parallel() cfg := &Config{ - Currency: CurrencyConfig{ - CryptocurrencyProvider: CryptocurrencyProvider{ + Currency: currency.Config{ + CryptocurrencyProvider: currency.Provider{ Name: "hellomoto", }, }, @@ -376,13 +376,13 @@ func TestGetCryptocurrencyProviderConfig(t *testing.T) { func TestUpdateCryptocurrencyProviderConfig(t *testing.T) { t.Parallel() cfg := &Config{ - Currency: CurrencyConfig{ - CryptocurrencyProvider: CryptocurrencyProvider{ + Currency: currency.Config{ + CryptocurrencyProvider: currency.Provider{ Name: "hellomoto", }, }, } - cfg.UpdateCryptocurrencyProviderConfig(CryptocurrencyProvider{Name: "SERIOUS TESTING PROCEDURE!"}) + cfg.UpdateCryptocurrencyProviderConfig(currency.Provider{Name: "SERIOUS TESTING PROCEDURE!"}) if cfg.Currency.CryptocurrencyProvider.Name != "SERIOUS TESTING PROCEDURE!" { t.Error("UpdateCurrencyProviderConfig LoadConfig error") } @@ -1194,8 +1194,8 @@ func TestCountEnabledExchanges(t *testing.T) { func TestGetCurrencyPairDisplayConfig(t *testing.T) { t.Parallel() cfg := &Config{ - Currency: CurrencyConfig{ - CurrencyPairFormat: &CurrencyPairFormatConfig{ + Currency: currency.Config{ + CurrencyPairFormat: ¤cy.PairFormat{ Delimiter: "-", Uppercase: true, }, @@ -1241,34 +1241,11 @@ func TestGetExchangeConfig(t *testing.T) { } } -func TestGetForexProviderConfig(t *testing.T) { - t.Parallel() - fxr := "Fixer" - cfg := &Config{ - Currency: CurrencyConfig{ - ForexProviders: []currency.FXSettings{ - { - Name: fxr, - }, - }, - }, - } - _, err := cfg.GetForexProvider(fxr) - if err != nil { - t.Error("GetForexProviderConfig error", err) - } - - _, err = cfg.GetForexProvider("this is not a forex provider") - if err == nil { - t.Error("GetForexProviderConfig no error for invalid provider") - } -} - func TestGetForexProviders(t *testing.T) { t.Parallel() fxr := "Fixer" cfg := &Config{ - Currency: CurrencyConfig{ + Currency: currency.Config{ ForexProviders: []currency.FXSettings{ { Name: fxr, @@ -1285,7 +1262,7 @@ func TestGetPrimaryForexProvider(t *testing.T) { t.Parallel() fxr := "Fixer" // nolint:ifshort,nolintlint // false positive and triggers only on Windows cfg := &Config{ - Currency: CurrencyConfig{ + Currency: currency.Config{ ForexProviders: []currency.FXSettings{ { Name: fxr, @@ -1700,52 +1677,6 @@ func TestCheckExchangeConfigValues(t *testing.T) { } } -func TestRetrieveConfigCurrencyPairs(t *testing.T) { - t.Parallel() - cp1 := currency.NewPair(currency.DOGE, currency.XRP) - cp2 := currency.NewPair(currency.DOGE, currency.USD) - cfg := &Config{ - Exchanges: []Exchange{ - { - Enabled: true, - BaseCurrencies: currency.Currencies{ - currency.USD, - }, - CurrencyPairs: ¤cy.PairsManager{ - RequestFormat: nil, - ConfigFormat: nil, - UseGlobalFormat: false, - LastUpdated: 0, - Pairs: map[asset.Item]*currency.PairStore{ - asset.Spot: { - AssetEnabled: convert.BoolPtr(true), - Available: currency.Pairs{cp1, cp2}, - Enabled: currency.Pairs{cp1}, - ConfigFormat: ¤cy.PairFormat{}, - RequestFormat: ¤cy.PairFormat{}, - }, - }, - }, - }, - }, - } - err := cfg.RetrieveConfigCurrencyPairs(true, asset.Spot) - if err != nil { - t.Errorf( - "TestRetrieveConfigCurrencyPairs.RetrieveConfigCurrencyPairs: %s", - err.Error(), - ) - } - - err = cfg.RetrieveConfigCurrencyPairs(false, asset.Spot) - if err != nil { - t.Errorf( - "TestRetrieveConfigCurrencyPairs.RetrieveConfigCurrencyPairs: %s", - err.Error(), - ) - } -} - func TestReadConfigFromFile(t *testing.T) { cfg := &Config{} err := cfg.ReadConfigFromFile(TestFile, true) @@ -1959,14 +1890,10 @@ func TestUpdateConfig(t *testing.T) { t.Fatalf("Error should have been thrown for invalid path") } - newCfg.Currency.Cryptocurrencies = currency.NewCurrenciesFromStringArray([]string{""}) err = c.UpdateConfig(TestFile, &newCfg, true) if err != nil { t.Errorf("%s", err) } - if c.Currency.Cryptocurrencies.Join() == "" { - t.Fatalf("Cryptocurrencies should have been repopulated") - } } func BenchmarkUpdateConfig(b *testing.B) { @@ -2112,10 +2039,10 @@ func TestCheckNTPConfig(t *testing.T) { func TestCheckCurrencyConfigValues(t *testing.T) { t.Parallel() cfg := &Config{ - Currency: CurrencyConfig{}, + Currency: currency.Config{}, } cfg.Currency.ForexProviders = nil - cfg.Currency.CryptocurrencyProvider = CryptocurrencyProvider{} + cfg.Currency.CryptocurrencyProvider = currency.Provider{} err := cfg.CheckCurrencyConfigValues() if err != nil { t.Error(err) @@ -2123,7 +2050,7 @@ func TestCheckCurrencyConfigValues(t *testing.T) { if cfg.Currency.ForexProviders == nil { t.Error("Failed to populate c.Currency.ForexProviders") } - if cfg.Currency.CryptocurrencyProvider.APIkey != DefaultUnsetAPIKey { + if cfg.Currency.CryptocurrencyProvider.APIKey != DefaultUnsetAPIKey { t.Error("Failed to set the api key to the default key") } if cfg.Currency.CryptocurrencyProvider.Name != "CoinMarketCap" { @@ -2133,34 +2060,29 @@ func TestCheckCurrencyConfigValues(t *testing.T) { cfg.Currency.ForexProviders[0].Enabled = true cfg.Currency.ForexProviders[0].Name = "CurrencyConverter" cfg.Currency.ForexProviders[0].PrimaryProvider = true - cfg.Currency.Cryptocurrencies = nil cfg.Cryptocurrencies = nil cfg.Currency.CurrencyPairFormat = nil - cfg.CurrencyPairFormat = &CurrencyPairFormatConfig{ + cfg.CurrencyPairFormat = ¤cy.PairFormat{ Uppercase: true, } - cfg.Currency.FiatDisplayCurrency = currency.Code{} + cfg.Currency.FiatDisplayCurrency = currency.EMPTYCODE cfg.FiatDisplayCurrency = ¤cy.BTC cfg.Currency.CryptocurrencyProvider.Enabled = true err = cfg.CheckCurrencyConfigValues() if err != nil { t.Error(err) } - if cfg.Currency.ForexProviders[0].Enabled { - t.Error("Failed to disable invalid forex provider") - } if !cfg.Currency.CurrencyPairFormat.Uppercase { t.Error("Failed to apply c.CurrencyPairFormat format to c.Currency.CurrencyPairFormat") } cfg.Currency.CryptocurrencyProvider.Enabled = false - cfg.Currency.CryptocurrencyProvider.APIkey = "" + cfg.Currency.CryptocurrencyProvider.APIKey = "" cfg.Currency.CryptocurrencyProvider.AccountPlan = "" cfg.FiatDisplayCurrency = ¤cy.BTC cfg.Currency.ForexProviders[0].Enabled = true cfg.Currency.ForexProviders[0].Name = "Name" cfg.Currency.ForexProviders[0].PrimaryProvider = true - cfg.Currency.Cryptocurrencies = currency.Currencies{} cfg.Cryptocurrencies = ¤cy.Currencies{} err = cfg.CheckCurrencyConfigValues() if err != nil { @@ -2169,7 +2091,7 @@ func TestCheckCurrencyConfigValues(t *testing.T) { if cfg.FiatDisplayCurrency != nil { t.Error("Failed to clear c.FiatDisplayCurrency") } - if cfg.Currency.CryptocurrencyProvider.APIkey != DefaultUnsetAPIKey || + if cfg.Currency.CryptocurrencyProvider.APIKey != DefaultUnsetAPIKey || cfg.Currency.CryptocurrencyProvider.AccountPlan != DefaultUnsetAccountPlan { t.Error("Failed to set CryptocurrencyProvider.APIkey and AccountPlan") } diff --git a/config/config_types.go b/config/config_types.go index 38810f3fb05..47f9be6623c 100644 --- a/config/config_types.go +++ b/config/config_types.go @@ -86,7 +86,7 @@ type Config struct { Profiler Profiler `json:"profiler"` NTPClient NTPClientConfig `json:"ntpclient"` GCTScript gctscript.Config `json:"gctscript"` - Currency CurrencyConfig `json:"currencyConfig"` + Currency currency.Config `json:"currencyConfig"` Communications base.CommunicationsConfig `json:"communications"` RemoteControl RemoteControlConfig `json:"remoteControl"` Portfolio portfolio.Base `json:"portfolioAddresses"` @@ -94,11 +94,11 @@ type Config struct { BankAccounts []banking.Account `json:"bankAccounts"` // Deprecated config settings, will be removed at a future date - Webserver *WebserverConfig `json:"webserver,omitempty"` - CurrencyPairFormat *CurrencyPairFormatConfig `json:"currencyPairFormat,omitempty"` - FiatDisplayCurrency *currency.Code `json:"fiatDispayCurrency,omitempty"` - Cryptocurrencies *currency.Currencies `json:"cryptocurrencies,omitempty"` - SMS *base.SMSGlobalConfig `json:"smsGlobal,omitempty"` + Webserver *WebserverConfig `json:"webserver,omitempty"` + CurrencyPairFormat *currency.PairFormat `json:"currencyPairFormat,omitempty"` + FiatDisplayCurrency *currency.Code `json:"fiatDispayCurrency,omitempty"` + Cryptocurrencies *currency.Currencies `json:"cryptocurrencies,omitempty"` + SMS *base.SMSGlobalConfig `json:"smsGlobal,omitempty"` // encryption session values storedSalt []byte sessionDK []byte @@ -249,26 +249,6 @@ type BankTransaction struct { PaymentInstructions string `json:"paymentInstructions"` } -// CurrencyConfig holds all the information needed for currency related manipulation -type CurrencyConfig struct { - ForexProviders []currency.FXSettings `json:"forexProviders"` - CryptocurrencyProvider CryptocurrencyProvider `json:"cryptocurrencyProvider"` - Cryptocurrencies currency.Currencies `json:"cryptocurrencies"` - CurrencyPairFormat *CurrencyPairFormatConfig `json:"currencyPairFormat"` - FiatDisplayCurrency currency.Code `json:"fiatDisplayCurrency"` - CurrencyFileUpdateDuration time.Duration `json:"currencyFileUpdateDuration"` - ForeignExchangeUpdateDuration time.Duration `json:"foreignExchangeUpdateDuration"` -} - -// CryptocurrencyProvider defines coinmarketcap tools -type CryptocurrencyProvider struct { - Name string `json:"name"` - Enabled bool `json:"enabled"` - Verbose bool `json:"verbose"` - APIkey string `json:"apiKey"` - AccountPlan string `json:"accountPlan"` -} - // FeaturesSupportedConfig stores the exchanges supported features type FeaturesSupportedConfig struct { REST bool `json:"restAPI"` diff --git a/config_example.json b/config_example.json index 93b12ed43e5..44a9c0911ce 100644 --- a/config_example.json +++ b/config_example.json @@ -26,7 +26,7 @@ "maxsize": 250 }, "advancedSettings": { - "showLogSystemName": false, + "showLogSystemName": true, "spacer": " | ", "timeStampFormat": "02/01/2006 15:04:05", "headers": { diff --git a/currency/code.go b/currency/code.go index 6a20915542f..6cc151faf9b 100644 --- a/currency/code.go +++ b/currency/code.go @@ -8,10 +8,21 @@ import ( "unicode" ) +var ( + // ErrCurrencyCodeEmpty defines an error if the currency code is empty + ErrCurrencyCodeEmpty = errors.New("currency code is empty") + errItemIsNil = errors.New("item is nil") + errItemIsEmpty = errors.New("item is empty") + errRoleUnset = errors.New("role unset") + + // EMPTYCODE is an empty currency code + EMPTYCODE = Code{} + // EMPTYPAIR is an empty currency pair + EMPTYPAIR = Pair{} +) + func (r Role) String() string { switch r { - case Unset: - return UnsetRoleString case Fiat: return FiatCurrencyString case Cryptocurrency: @@ -20,8 +31,10 @@ func (r Role) String() string { return TokenString case Contract: return ContractString + case Stable: + return StableString default: - return "UNKNOWN" + return UnsetRoleString } } @@ -49,6 +62,8 @@ func (r *Role) UnmarshalJSON(d []byte) error { *r = Token case ContractString: *r = Contract + case StableString: + *r = Stable default: return fmt.Errorf("unmarshal error role type %s unsupported for currency", incoming) @@ -69,20 +84,21 @@ func (b *BaseCodes) GetFullCurrencyData() (File, error) { for i := range b.Items { switch b.Items[i].Role { case Unset: - file.UnsetCurrency = append(file.UnsetCurrency, *b.Items[i]) + file.UnsetCurrency = append(file.UnsetCurrency, b.Items[i]) case Fiat: - file.FiatCurrency = append(file.FiatCurrency, *b.Items[i]) + file.FiatCurrency = append(file.FiatCurrency, b.Items[i]) case Cryptocurrency: - file.Cryptocurrency = append(file.Cryptocurrency, *b.Items[i]) + file.Cryptocurrency = append(file.Cryptocurrency, b.Items[i]) case Token: - file.Token = append(file.Token, *b.Items[i]) + file.Token = append(file.Token, b.Items[i]) case Contract: - file.Contracts = append(file.Contracts, *b.Items[i]) + file.Contracts = append(file.Contracts, b.Items[i]) + case Stable: + file.Stable = append(file.Stable, b.Items[i]) default: return file, errors.New("role undefined") } } - file.LastMainUpdate = b.LastMainUpdate.Unix() return file, nil } @@ -90,12 +106,10 @@ func (b *BaseCodes) GetFullCurrencyData() (File, error) { // GetCurrencies gets the full currency list from the base code type available // from the currency system func (b *BaseCodes) GetCurrencies() Currencies { - var currencies Currencies b.mtx.Lock() + currencies := make(Currencies, len(b.Items)) for i := range b.Items { - currencies = append(currencies, Code{ - Item: b.Items[i], - }) + currencies[i] = Code{Item: b.Items[i]} } b.mtx.Unlock() return currencies @@ -104,31 +118,27 @@ func (b *BaseCodes) GetCurrencies() Currencies { // UpdateCurrency updates or registers a currency/contract func (b *BaseCodes) UpdateCurrency(fullName, symbol, blockchain string, id int, r Role) error { if r == Unset { - return fmt.Errorf("role cannot be unset in update currency for %s", symbol) + return fmt.Errorf("cannot update currency %w for %s", errRoleUnset, symbol) } b.mtx.Lock() defer b.mtx.Unlock() for i := range b.Items { - if b.Items[i].Symbol != symbol { + if b.Items[i].Symbol != symbol || (b.Items[i].Role != Unset && b.Items[i].Role != r) { continue } - if b.Items[i].Role == Unset { + if fullName != "" { b.Items[i].FullName = fullName + } + if r != Unset { b.Items[i].Role = r + } + if blockchain != "" { b.Items[i].AssocChain = blockchain - b.Items[i].ID = id - return nil } - - if b.Items[i].Role != r { - // Captures same name currencies and duplicates to different roles - break + if id != 0 { + b.Items[i].ID = id } - - b.Items[i].FullName = fullName - b.Items[i].AssocChain = blockchain - b.Items[i].ID = id return nil } @@ -142,75 +152,70 @@ func (b *BaseCodes) UpdateCurrency(fullName, symbol, blockchain string, id int, return nil } -// Register registers a currency from a string and returns a currency code -func (b *BaseCodes) Register(c string) Code { - var format bool - if c != "" { - format = unicode.IsUpper(rune(c[0])) +// Register registers a currency from a string and returns a currency code, this +// can optionally include a role when it is known. +func (b *BaseCodes) Register(c string, newRole Role) Code { + if c == "" { + return EMPTYCODE } - // Force upper string storage and matching - c = strings.ToUpper(c) - b.mtx.Lock() - defer b.mtx.Unlock() - for i := range b.Items { - if b.Items[i].Symbol == c { - return Code{ - Item: b.Items[i], - UpperCase: format, - } + var format bool + // Digits fool upper and lower casing. So find first letter and check case. + for x := range c { + if !unicode.IsDigit(rune(c[x])) { + format = unicode.IsUpper(rune(c[x])) + break } } - newItem := &Item{Symbol: c} - b.Items = append(b.Items, newItem) - - return Code{ - Item: newItem, - UpperCase: format, - } -} - -// RegisterFiat registers a fiat currency from a string and returns a currency -// code -func (b *BaseCodes) RegisterFiat(c string) Code { + // Force upper string storage and matching c = strings.ToUpper(c) b.mtx.Lock() defer b.mtx.Unlock() for i := range b.Items { - if b.Items[i].Symbol == c { - if b.Items[i].Role == Unset { - b.Items[i].Role = Fiat - } + if b.Items[i].Symbol != c { + continue + } - if b.Items[i].Role != Fiat { + if newRole != Unset { + if b.Items[i].Role == Unset { + b.Items[i].Role = newRole + } else if b.Items[i].Role != newRole { + // This will duplicate item with same name but different role. + // TODO: This will need a specific update to NewCode to add in + // a specific param to find the exact name and role. continue } - return Code{Item: b.Items[i], UpperCase: true} } - } - item := &Item{Symbol: c, Role: Fiat} - b.Items = append(b.Items, item) - return Code{Item: item, UpperCase: true} + return Code{Item: b.Items[i], UpperCase: format} + } + newItem := &Item{Symbol: c, Lower: strings.ToLower(c), Role: newRole} + b.Items = append(b.Items, newItem) + return Code{Item: newItem, UpperCase: format} } // LoadItem sets item data func (b *BaseCodes) LoadItem(item *Item) error { + if item == nil { + return errItemIsNil + } + + if *item == (Item{}) { + return errItemIsEmpty + } + + item.Symbol = strings.ToUpper(item.Symbol) + item.Lower = strings.ToLower(item.Symbol) + b.mtx.Lock() defer b.mtx.Unlock() for i := range b.Items { if b.Items[i].Symbol != item.Symbol || - (b.Items[i].Role != Unset && - item.Role != Unset && - b.Items[i].Role != item.Role) { + (b.Items[i].Role != Unset && item.Role != Unset && b.Items[i].Role != item.Role) { continue } - b.Items[i].AssocChain = item.AssocChain - b.Items[i].ID = item.ID - b.Items[i].Role = item.Role - b.Items[i].FullName = item.FullName return nil } b.Items = append(b.Items, item) @@ -224,7 +229,7 @@ func NewCode(c string) Code { // String conforms to the stringer interface func (i *Item) String() string { - return i.FullName + return i.Symbol } // String converts the code to string @@ -232,11 +237,10 @@ func (c Code) String() string { if c.Item == nil { return "" } - if c.UpperCase { - return strings.ToUpper(c.Item.Symbol) + return c.Item.Symbol } - return strings.ToLower(c.Item.Symbol) + return c.Item.Lower } // Lower converts the code to lowercase formatting @@ -272,35 +276,27 @@ func (c Code) MarshalJSON() ([]byte, error) { // IsEmpty returns true if the code is empty func (c Code) IsEmpty() bool { - if c.Item == nil { - return true - } - return c.Item.Symbol == "" + return c.Item == nil || c.Item.Symbol == "" } -// Match returns if the code supplied is the same as the corresponding code -func (c Code) Match(check Code) bool { +// Equal returns if the code supplied is the same as the corresponding code +func (c Code) Equal(check Code) bool { return c.Item == check.Item } -// IsDefaultFiatCurrency checks if the currency passed in matches the default -// fiat currency -func (c Code) IsDefaultFiatCurrency() bool { - return storage.IsDefaultCurrency(c) -} - -// IsDefaultCryptocurrency checks if the currency passed in matches the default -// cryptocurrency -func (c Code) IsDefaultCryptocurrency() bool { - return storage.IsDefaultCryptocurrency(c) -} - // IsFiatCurrency checks if the currency passed is an enabled fiat currency func (c Code) IsFiatCurrency() bool { - return storage.IsFiatCurrency(c) + return c.Item != nil && c.Item.Role == Fiat } // IsCryptocurrency checks if the currency passed is an enabled CRYPTO currency. +// NOTE: All unset currencies will default to cryptocurrencies and stable coins +// are cryptocurrencies as well. func (c Code) IsCryptocurrency() bool { - return storage.IsCryptocurrency(c) + return c.Item != nil && c.Item.Role&(Cryptocurrency|Stable) == c.Item.Role +} + +// IsStableCurrency checks if the currency is a stable currency. +func (c Code) IsStableCurrency() bool { + return c.Item != nil && c.Item.Role == Stable } diff --git a/currency/code_test.go b/currency/code_test.go index 779f69d37fb..af49d49a427 100644 --- a/currency/code_test.go +++ b/currency/code_test.go @@ -2,6 +2,7 @@ package currency import ( "encoding/json" + "errors" "testing" ) @@ -38,7 +39,7 @@ func TestRoleString(t *testing.T) { var random Role = 1 << 7 - if random.String() != "UNKNOWN" { + if random.String() != UnsetRoleString { t.Errorf("Role String() error expected %s but received %s", "UNKNOWN", random) @@ -51,7 +52,7 @@ func TestRoleMarshalJSON(t *testing.T) { t.Error("Role MarshalJSON() error", err) } - if expected := `"fiatCurrency"`; string(d) != expected { + if expected := `"fiatcurrency"`; string(d) != expected { t.Errorf("Role MarshalJSON() error expected %s but received %s", expected, string(d)) @@ -66,6 +67,7 @@ func TestRoleUnmarshalJSON(t *testing.T) { RoleThree Role `json:"RoleThree"` RoleFour Role `json:"RoleFour"` RoleFive Role `json:"RoleFive"` + RoleSix Role `json:"RoleSix"` RoleUnknown Role `json:"RoleUnknown"` } @@ -75,6 +77,7 @@ func TestRoleUnmarshalJSON(t *testing.T) { RoleThree: Fiat, RoleFour: Token, RoleFive: Contract, + RoleSix: Stable, } e, err := json.Marshal(1337) @@ -138,6 +141,27 @@ func TestRoleUnmarshalJSON(t *testing.T) { if err == nil { t.Error("Expected unmarshall error") } + + err = unhandled.UnmarshalJSON([]byte(`1336`)) + if err == nil { + t.Error("Expected unmarshall error") + } +} + +func (b *BaseCodes) assertRole(t *testing.T, c Code, r Role) { + t.Helper() + b.mtx.Lock() + defer b.mtx.Unlock() + for x := range b.Items { + if b.Items[x] != c.Item { + continue + } + if b.Items[x].Role != r { + t.Fatal("unexpected role") + } + return + } + t.Fatal("code pointer not found") } func TestBaseCode(t *testing.T) { @@ -147,35 +171,66 @@ func TestBaseCode(t *testing.T) { main.HasData()) } - catsCode := main.Register("CATS") + catsUnset := main.Register("CATS", Unset) + main.assertRole(t, catsUnset, Unset) if !main.HasData() { t.Errorf("BaseCode HasData() error expected true but received %v", main.HasData()) } - if !main.Register("CATS").Match(catsCode) { + // Changes unset to fiat + catsFiat := main.Register("CATS", Fiat) + main.assertRole(t, catsUnset, Fiat) + + // Register as unset, will return first match. + otherFiatCat := main.Register("CATS", Unset) + main.assertRole(t, otherFiatCat, Fiat) + if !otherFiatCat.Equal(catsFiat) { + t.Errorf("BaseCode Match() error expected true but received %v", + false) + } + + // Register as fiat, will return fiat match. + thatOtherFiatCat := main.Register("CATS", Fiat) + main.assertRole(t, otherFiatCat, Fiat) + if !thatOtherFiatCat.Equal(catsFiat) { t.Errorf("BaseCode Match() error expected true but received %v", false) } - if main.Register("DOGS").Match(catsCode) { + // Register as stable, will return a different currency with the same + // currency code. + superStableCatNoShakes := main.Register("CATS", Stable) + main.assertRole(t, superStableCatNoShakes, Stable) + if superStableCatNoShakes.Equal(catsFiat) { + t.Errorf("BaseCode Match() error expected true but received %v", + true) + } + + // Due to the role being unset originally, this will be set to Fiat when + // explicitly set. + if !catsUnset.Equal(catsFiat) { + t.Fatal("both should be the same") + } + + if main.Register("DOGS", Unset).Equal(catsUnset) { t.Errorf("BaseCode Match() error expected false but received %v", true) } loadedCurrencies := main.GetCurrencies() - if loadedCurrencies.Contains(main.Register("OWLS")) { + if loadedCurrencies.Contains(main.Register("OWLS", Unset)) { t.Errorf("BaseCode Contains() error expected false but received %v", true) } - if !loadedCurrencies.Contains(catsCode) { + if !loadedCurrencies.Contains(catsFiat) { t.Errorf("BaseCode Contains() error expected true but received %v", false) } - main.Register("XBTUSD") + main.Register("XBTUSD", Unset) err := main.UpdateCurrency("Bitcoin Perpetual", "XBTUSD", @@ -186,19 +241,24 @@ func TestBaseCode(t *testing.T) { t.Fatal(err) } - main.Register("BTC") + main.Register("BTC", Unset) + err = main.UpdateCurrency("Bitcoin", "BTC", "", 1337, Unset) + if !errors.Is(err, errRoleUnset) { + t.Fatalf("received: '%v' but expected: '%v'", err, errRoleUnset) + } + err = main.UpdateCurrency("Bitcoin", "BTC", "", 1337, Cryptocurrency) if err != nil { t.Fatal(err) } - main.Register("AUD") + aud := main.Register("AUD", Unset) err = main.UpdateCurrency("Unreal Dollar", "AUD", "", 1111, Fiat) if err != nil { t.Fatal(err) } - if main.Items[5].FullName != "Unreal Dollar" { + if aud.Item.FullName != "Unreal Dollar" { t.Error("Expected fullname to update for AUD") } @@ -207,22 +267,22 @@ func TestBaseCode(t *testing.T) { t.Fatal(err) } - main.Items[5].Role = Unset + aud.Item.Role = Unset err = main.UpdateCurrency("Australian Dollar", "AUD", "", 1336, Fiat) if err != nil { t.Fatal(err) } - if main.Items[5].Role != Fiat { + if aud.Item.Role != Fiat { t.Error("Expected role to change to Fiat") } - main.Register("PPT") + main.Register("PPT", Unset) err = main.UpdateCurrency("Populous", "PPT", "ETH", 1335, Token) if err != nil { t.Fatal(err) } - contract := main.Register("XBTUSD") + contract := main.Register("XBTUSD", Unset) if contract.IsFiatCurrency() { t.Errorf("BaseCode IsFiatCurrency() error expected false but received %v", @@ -230,18 +290,28 @@ func TestBaseCode(t *testing.T) { } if contract.IsCryptocurrency() { - t.Errorf("BaseCode IsFiatCurrency() error expected false but received %v", + t.Errorf("BaseCode IsCryptocurrency() error expected false but received %v", true) } - if contract.IsDefaultFiatCurrency() { - t.Errorf("BaseCode IsDefaultFiatCurrency() error expected false but received %v", - true) + err = main.LoadItem(nil) + if !errors.Is(err, errItemIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errItemIsNil) } - if contract.IsDefaultFiatCurrency() { - t.Errorf("BaseCode IsFiatCurrency() error expected false but received %v", - true) + err = main.LoadItem(&Item{}) + if !errors.Is(err, errItemIsEmpty) { + t.Fatalf("received: '%v' but expected: '%v'", err, errItemIsEmpty) + } + + err = main.LoadItem(&Item{ + ID: 0, + FullName: "Cardano", + Role: Cryptocurrency, + Symbol: "ADA", + }) + if err != nil { + t.Fatal(err) } err = main.LoadItem(&Item{ @@ -269,7 +339,7 @@ func TestBaseCode(t *testing.T) { len(full.Cryptocurrency)) } - if len(full.FiatCurrency) != 1 { + if len(full.FiatCurrency) != 2 { t.Errorf("BaseCode GetFullCurrencyData() error expected 1 but received %v", len(full.FiatCurrency)) } @@ -279,7 +349,7 @@ func TestBaseCode(t *testing.T) { len(full.Token)) } - if len(full.UnsetCurrency) != 3 { + if len(full.UnsetCurrency) != 2 { t.Errorf("BaseCode GetFullCurrencyData() error expected 3 but received %v", len(full.UnsetCurrency)) } @@ -304,24 +374,25 @@ func TestBaseCode(t *testing.T) { } main.Items[0].FullName = "Hello" - err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency) + err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Fiat) if err != nil { t.Fatal(err) } + if main.Items[0].FullName != "MEWOW" { t.Error("Fullname not updated") } - err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency) + err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Fiat) if err != nil { t.Fatal(err) } - err = main.UpdateCurrency("WOWCATS", "CATS", "", 3, Token) + err = main.UpdateCurrency("WOWCATS", "CATS", "", 3, Fiat) if err != nil { t.Fatal(err) } // Creates a new item under a different currency role - if main.Items[9].ID != 3 { + if main.Items[0].ID != 3 { t.Error("ID not updated") } @@ -381,6 +452,15 @@ func TestCodeUnmarshalJSON(t *testing.T) { expected, unmarshalHere) } + + encoded, err = json.Marshal(1336) // :'( + if err != nil { + t.Fatal(err) + } + err = json.Unmarshal(encoded, &unmarshalHere) + if err == nil { + t.Fatal("expected error") + } } func TestCodeMarshalJSON(t *testing.T) { @@ -406,7 +486,7 @@ func TestCodeMarshalJSON(t *testing.T) { quickstruct = struct { Codey Code `json:"sweetCodes"` }{ - Codey: Code{}, // nil code + Codey: EMPTYCODE, // nil code } encoded, err = json.Marshal(quickstruct) @@ -421,37 +501,11 @@ func TestCodeMarshalJSON(t *testing.T) { } } -func TestIsDefaultCurrency(t *testing.T) { - if !USD.IsDefaultFiatCurrency() { - t.Errorf("TestIsDefaultCurrency Cannot match currency %s.", - USD) - } - if !AUD.IsDefaultFiatCurrency() { - t.Errorf("TestIsDefaultCurrency Cannot match currency, %s.", - AUD) - } - if LTC.IsDefaultFiatCurrency() { - t.Errorf("TestIsDefaultCurrency Function return is incorrect with, %s.", - LTC) - } -} - -func TestIsDefaultCryptocurrency(t *testing.T) { - if !BTC.IsDefaultCryptocurrency() { - t.Errorf("TestIsDefaultCryptocurrency cannot match currency, %s.", - BTC) - } - if !LTC.IsDefaultCryptocurrency() { - t.Errorf("TestIsDefaultCryptocurrency cannot match currency, %s.", - LTC) - } - if AUD.IsDefaultCryptocurrency() { - t.Errorf("TestIsDefaultCryptocurrency function return is incorrect with, %s.", - AUD) - } -} - func TestIsFiatCurrency(t *testing.T) { + if EMPTYCODE.IsFiatCurrency() { + t.Errorf("TestIsFiatCurrency cannot match currency, %s.", + EMPTYCODE) + } if !USD.IsFiatCurrency() { t.Errorf( "TestIsFiatCurrency cannot match currency, %s.", USD) @@ -465,31 +519,74 @@ func TestIsFiatCurrency(t *testing.T) { "TestIsFiatCurrency cannot match currency, %s.", LINO, ) } + if USDT.IsFiatCurrency() { + t.Errorf( + "TestIsFiatCurrency cannot match currency, %s.", USD) + } + if DAI.IsFiatCurrency() { + t.Errorf( + "TestIsFiatCurrency cannot match currency, %s.", USD) + } } func TestIsCryptocurrency(t *testing.T) { + if EMPTYCODE.IsCryptocurrency() { + t.Errorf("TestIsCryptocurrency cannot match currency, %s.", + EMPTYCODE) + } if !BTC.IsCryptocurrency() { - t.Errorf("TestIsFiatCurrency cannot match currency, %s.", + t.Errorf("TestIsCryptocurrency cannot match currency, %s.", BTC) } if !LTC.IsCryptocurrency() { - t.Errorf("TestIsFiatCurrency cannot match currency, %s.", + t.Errorf("TestIsCryptocurrency cannot match currency, %s.", LTC) } if AUD.IsCryptocurrency() { - t.Errorf("TestIsFiatCurrency cannot match currency, %s.", + t.Errorf("TestIsCryptocurrency cannot match currency, %s.", AUD) } + if !USDT.IsCryptocurrency() { + t.Errorf( + "TestIsCryptocurrency cannot match currency, %s.", USD) + } + if !DAI.IsCryptocurrency() { + t.Errorf( + "TestIsCryptocurrency cannot match currency, %s.", USD) + } +} + +func TestIsStableCurrency(t *testing.T) { + if EMPTYCODE.IsStableCurrency() { + t.Errorf("TestIsStableCurrency cannot match currency, %s.", EMPTYCODE) + } + if BTC.IsStableCurrency() { + t.Errorf("TestIsStableCurrency cannot match currency, %s.", BTC) + } + if LTC.IsStableCurrency() { + t.Errorf("TestIsStableCurrency cannot match currency, %s.", LTC) + } + if AUD.IsStableCurrency() { + t.Errorf("TestIsStableCurrency cannot match currency, %s.", AUD) + } + if !USDT.IsStableCurrency() { + t.Errorf("TestIsStableCurrency cannot match currency, %s.", USDT) + } + if !DAI.IsStableCurrency() { + t.Errorf("TestIsStableCurrency cannot match currency, %s.", DAI) + } } func TestItemString(t *testing.T) { - expected := "Hello,World" newItem := Item{ - FullName: expected, + ID: 1337, + FullName: "Hello,World", + Symbol: "HWORLD", + AssocChain: "Silly", } - if newItem.String() != expected { - t.Errorf("Currency String() error expected %s but received %s", + if expected := "HWORLD"; newItem.String() != expected { + t.Errorf("Currency String() error expected '%s' but received '%s'", expected, &newItem) } diff --git a/currency/code_types.go b/currency/code_types.go index 0d46e78fb09..d4997952798 100644 --- a/currency/code_types.go +++ b/currency/code_types.go @@ -12,12 +12,14 @@ const ( Cryptocurrency Token Contract + Stable UnsetRoleString = "roleUnset" - FiatCurrencyString = "fiatCurrency" + FiatCurrencyString = "fiatcurrency" CryptocurrencyString = "cryptocurrency" TokenString = "token" ContractString = "contract" + StableString = "stablecurrency" ) // Role defines a bitmask for the full currency roles either; fiat, @@ -34,1636 +36,3044 @@ type BaseCodes struct { // Code defines an ISO 4217 fiat currency or unofficial cryptocurrency code // string type Code struct { - Item *Item + Item *Item + // TODO: Below will force the use of the Equal method for comparison. Big + // job to update all maps and instances through the code base. + // _ []struct{} UpperCase bool } // Item defines a sub type containing the main attributes of a designated // currency code pointer type Item struct { - ID int `json:"id,omitempty"` - FullName string `json:"fullName,omitempty"` - Symbol string `json:"symbol"` - Role Role `json:"-"` + ID int `json:"id,omitempty"` + FullName string `json:"fullName,omitempty"` + Symbol string `json:"symbol"` + // Lower is the lower case symbol for optimization purposes so no need to + // rely on the strings package to upper and lower strings when it is not + // needed + Lower string `json:"-"` + Role Role `json:"role"` AssocChain string `json:"associatedBlockchain,omitempty"` } +// Lock implements the sync.Locker interface and forces a govet check nocopy +func (*Item) Lock() {} + +// Unlock implements the sync.Locker interface and forces a govet check nocopy +func (*Item) Unlock() {} + // Const declarations for individual currencies/tokens/fiat // An ever growing list. Cares not for equivalence, just is var ( - BTC = NewCode("BTC") - LTC = NewCode("LTC") - ETH = NewCode("ETH") - XRP = NewCode("XRP") - BCH = NewCode("BCH") - EOS = NewCode("EOS") - XLM = NewCode("XLM") - USDT = NewCode("USDT") - USDC = NewCode("USDC") - ADA = NewCode("ADA") - XMR = NewCode("XMR") - TRX = NewCode("TRX") - MIOTA = NewCode("MIOTA") - DASH = NewCode("DASH") - BNB = NewCode("BNB") - NEO = NewCode("NEO") - ETC = NewCode("ETC") - XEM = NewCode("XEM") - XTZ = NewCode("XTZ") - VET = NewCode("VET") - DOGE = NewCode("DOGE") - ZEC = NewCode("ZEC") - OMG = NewCode("OMG") - BTG = NewCode("BTG") - MKR = NewCode("MKR") - BCN = NewCode("BCN") - ONT = NewCode("ONT") - ZRX = NewCode("ZRX") - LSK = NewCode("LSK") - DCR = NewCode("DCR") - QTUM = NewCode("QTUM") - BCD = NewCode("BCD") - BTS = NewCode("BTS") - NANO = NewCode("NANO") - ZIL = NewCode("ZIL") - SC = NewCode("SC") - DGB = NewCode("DGB") - ICX = NewCode("ICX") - STEEM = NewCode("STEEM") - AE = NewCode("AE") - XVG = NewCode("XVG") - WAVES = NewCode("WAVES") - NPXS = NewCode("NPXS") - ETN = NewCode("ETN") - BTM = NewCode("BTM") - BAT = NewCode("BAT") - ETP = NewCode("ETP") - HOT = NewCode("HOT") - STRAT = NewCode("STRAT") // nolint // Cryptocurrency code - GNT = NewCode("GNT") - REP = NewCode("REP") - SNT = NewCode("SNT") - PPT = NewCode("PPT") - KMD = NewCode("KMD") - TUSD = NewCode("TUSD") - CNX = NewCode("CNX") - LINK = NewCode("LINK") - WTC = NewCode("WTC") - ARDR = NewCode("ARDR") - WAN = NewCode("WAN") - MITH = NewCode("MITH") - RDD = NewCode("RDD") - IOST = NewCode("IOST") - IOT = NewCode("IOT") - KCS = NewCode("KCS") - MAID = NewCode("MAID") - XET = NewCode("XET") - MOAC = NewCode("MOAC") - HC = NewCode("HC") - AION = NewCode("AION") - AOA = NewCode("AOA") - HT = NewCode("HT") - ELF = NewCode("ELF") - LRC = NewCode("LRC") - BNT = NewCode("BNT") - CMT = NewCode("CMT") - DGD = NewCode("DGD") - DCN = NewCode("DCN") - FUN = NewCode("FUN") - GXS = NewCode("GXS") - DROP = NewCode("DROP") - MANA = NewCode("MANA") - PAY = NewCode("PAY") - MCO = NewCode("MCO") - THETA = NewCode("THETA") - NXT = NewCode("NXT") - NOAH = NewCode("NOAH") - LOOM = NewCode("LOOM") - POWR = NewCode("POWR") - WAX = NewCode("WAX") - ELA = NewCode("ELA") - PIVX = NewCode("PIVX") - XIN = NewCode("XIN") - DAI = NewCode("DAI") - BTCP = NewCode("BTCP") - NEXO = NewCode("NEXO") - XBT = NewCode("XBT") - SAN = NewCode("SAN") - GAS = NewCode("GAS") - BCC = NewCode("BCC") - HCC = NewCode("HCC") - OAX = NewCode("OAX") - DNT = NewCode("DNT") - ICN = NewCode("ICN") - LLT = NewCode("LLT") - YOYO = NewCode("YOYO") - SNGLS = NewCode("SNGLS") - BQX = NewCode("BQX") - KNC = NewCode("KNC") - SNM = NewCode("SNM") - CTR = NewCode("CTR") - SALT = NewCode("SALT") - MDA = NewCode("MDA") - IOTA = NewCode("IOTA") - SUB = NewCode("SUB") - MTL = NewCode("MTL") - MTH = NewCode("MTH") - ENG = NewCode("ENG") - AST = NewCode("AST") - CLN = NewCode("CLN") - EDG = NewCode("EDG") - FIRST = NewCode("1ST") - GOLOS = NewCode("GOLOS") - ANT = NewCode("ANT") - GBG = NewCode("GBG") - HMQ = NewCode("HMQ") - INCNT = NewCode("INCNT") - ACE = NewCode("ACE") - ACT = NewCode("ACT") - AAC = NewCode("AAC") - AIDOC = NewCode("AIDOC") - SOC = NewCode("SOC") - ATL = NewCode("ATL") - AVT = NewCode("AVT") - BKX = NewCode("BKX") - BEC = NewCode("BEC") - VEE = NewCode("VEE") - PTOY = NewCode("PTOY") - CAG = NewCode("CAG") - CIC = NewCode("CIC") - CBT = NewCode("CBT") - CAN = NewCode("CAN") - DAT = NewCode("DAT") - DNA = NewCode("DNA") - INT = NewCode("INT") - IPC = NewCode("IPC") - ILA = NewCode("ILA") - LIGHT = NewCode("LIGHT") - MAG = NewCode("MAG") - AMM = NewCode("AMM") - MOF = NewCode("MOF") - MGC = NewCode("MGC") - OF = NewCode("OF") - LA = NewCode("LA") - LEV = NewCode("LEV") - NGC = NewCode("NGC") - OKB = NewCode("OKB") - MOT = NewCode("MOT") - PRA = NewCode("PRA") - R = NewCode("R") - SSC = NewCode("SSC") - SHOW = NewCode("SHOW") - SPF = NewCode("SPF") - SNC = NewCode("SNC") - SWFTC = NewCode("SWFTC") - TRA = NewCode("TRA") - TOPC = NewCode("TOPC") - TRIO = NewCode("TRIO") - QVT = NewCode("QVT") - UCT = NewCode("UCT") - UKG = NewCode("UKG") - UTK = NewCode("UTK") - VIU = NewCode("VIU") - WFEE = NewCode("WFEE") - WRC = NewCode("WRC") - UGC = NewCode("UGC") - YEE = NewCode("YEE") - YOYOW = NewCode("YOYOW") - ZIP = NewCode("ZIP") - READ = NewCode("READ") - RCT = NewCode("RCT") - REF = NewCode("REF") - XUC = NewCode("XUC") - FAIR = NewCode("FAIR") - GSC = NewCode("GSC") - HMC = NewCode("HMC") - PLU = NewCode("PLU") - PRO = NewCode("PRO") - QRL = NewCode("QRL") - REN = NewCode("REN") - ROUND = NewCode("ROUND") - SRN = NewCode("SRN") - XID = NewCode("XID") - SBD = NewCode("SBD") - TAAS = NewCode("TAAS") - TKN = NewCode("TKN") - VEN = NewCode("VEN") - VSL = NewCode("VSL") - TRST = NewCode("TRST") - XXX = NewCode("XXX") - IND = NewCode("IND") - LDC = NewCode("LDC") - GUP = NewCode("GUP") - MGO = NewCode("MGO") - MYST = NewCode("MYST") - NEU = NewCode("NEU") - NET = NewCode("NET") - BMC = NewCode("BMC") - BCAP = NewCode("BCAP") - TIME = NewCode("TIME") - CFI = NewCode("CFI") - EVX = NewCode("EVX") - REQ = NewCode("REQ") - VIB = NewCode("VIB") - ARK = NewCode("ARK") - MOD = NewCode("MOD") - ENJ = NewCode("ENJ") - STORJ = NewCode("STORJ") - RCN = NewCode("RCN") - NULS = NewCode("NULS") - RDN = NewCode("RDN") - DLT = NewCode("DLT") - AMB = NewCode("AMB") - BCPT = NewCode("BCPT") - ARN = NewCode("ARN") - GVT = NewCode("GVT") - CDT = NewCode("CDT") - POE = NewCode("POE") - QSP = NewCode("QSP") - XZC = NewCode("XZC") - TNT = NewCode("TNT") - FUEL = NewCode("FUEL") - ADX = NewCode("ADX") - CND = NewCode("CND") - LEND = NewCode("LEND") - WABI = NewCode("WABI") - SBTC = NewCode("SBTC") - BCX = NewCode("BCX") - TNB = NewCode("TNB") - GTO = NewCode("GTO") - OST = NewCode("OST") - CVC = NewCode("CVC") - DATA = NewCode("DATA") - ETF = NewCode("ETF") - BRD = NewCode("BRD") - NEBL = NewCode("NEBL") - VIBE = NewCode("VIBE") - LUN = NewCode("LUN") - CHAT = NewCode("CHAT") - RLC = NewCode("RLC") - INS = NewCode("INS") - VIA = NewCode("VIA") - BLZ = NewCode("BLZ") - SYS = NewCode("SYS") - NCASH = NewCode("NCASH") - POA = NewCode("POA") - STORM = NewCode("STORM") - WPR = NewCode("WPR") - QLC = NewCode("QLC") - GRS = NewCode("GRS") - CLOAK = NewCode("CLOAK") - ZEN = NewCode("ZEN") - SKY = NewCode("SKY") - IOTX = NewCode("IOTX") - QKC = NewCode("QKC") - AGI = NewCode("AGI") - NXS = NewCode("NXS") - EON = NewCode("EON") - KEY = NewCode("KEY") - NAS = NewCode("NAS") - ADD = NewCode("ADD") - MEETONE = NewCode("MEETONE") - ATD = NewCode("ATD") - MFT = NewCode("MFT") - EOP = NewCode("EOP") - DENT = NewCode("DENT") - IQ = NewCode("IQ") - DOCK = NewCode("DOCK") - POLY = NewCode("POLY") - VTHO = NewCode("VTHO") - ONG = NewCode("ONG") - PHX = NewCode("PHX") - GO = NewCode("GO") - PAX = NewCode("PAX") - PAXG = NewCode("PAXG") - EDO = NewCode("EDO") - WINGS = NewCode("WINGS") - NAV = NewCode("NAV") - TRIG = NewCode("TRIG") - APPC = NewCode("APPC") - KRW = NewCode("KRW") - HSR = NewCode("HSR") - ETHOS = NewCode("ETHOS") - CTXC = NewCode("CTXC") - ITC = NewCode("ITC") - TRUE = NewCode("TRUE") - ABT = NewCode("ABT") - RNT = NewCode("RNT") - PLY = NewCode("PLY") - PST = NewCode("PST") - KICK = NewCode("KICK") - BTCZ = NewCode("BTCZ") - DXT = NewCode("DXT") - STQ = NewCode("STQ") - INK = NewCode("INK") - HBZ = NewCode("HBZ") - USDT_ETH = NewCode("USDT_ETH") // nolint // Cryptocurrency code - QTUM_ETH = NewCode("QTUM_ETH") // nolint // Cryptocurrency code - BTM_ETH = NewCode("BTM_ETH") // nolint // Cryptocurrency code - FIL = NewCode("FIL") - STX = NewCode("STX") - BOT = NewCode("BOT") - VERI = NewCode("VERI") - ZSC = NewCode("ZSC") - QBT = NewCode("QBT") - MED = NewCode("MED") - QASH = NewCode("QASH") - MDS = NewCode("MDS") - GOD = NewCode("GOD") - SMT = NewCode("SMT") - BTF = NewCode("BTF") - NAS_ETH = NewCode("NAS_ETH") // nolint // Cryptocurrency code - TSL = NewCode("TSL") - BIFI = NewCode("BIFI") - BNTY = NewCode("BNTY") - DRGN = NewCode("DRGN") - GTC = NewCode("GTC") - MDT = NewCode("MDT") - QUN = NewCode("QUN") - GNX = NewCode("GNX") - DDD = NewCode("DDD") - BTO = NewCode("BTO") - TIO = NewCode("TIO") - OCN = NewCode("OCN") - RUFF = NewCode("RUFF") - TNC = NewCode("TNC") - SNET = NewCode("SNET") - COFI = NewCode("COFI") - ZPT = NewCode("ZPT") - JNT = NewCode("JNT") - MTN = NewCode("MTN") - GEM = NewCode("GEM") - DADI = NewCode("DADI") - RFR = NewCode("RFR") - MOBI = NewCode("MOBI") - LEDU = NewCode("LEDU") - DBC = NewCode("DBC") - MKR_OLD = NewCode("MKR_OLD") // nolint // Cryptocurrency code - DPY = NewCode("DPY") - BCDN = NewCode("BCDN") - EOSDAC = NewCode("EOSDAC") - TIPS = NewCode("TIPS") - XMC = NewCode("XMC") - PPS = NewCode("PPS") - BOE = NewCode("BOE") - MEDX = NewCode("MEDX") - SMT_ETH = NewCode("SMT_ETH") // nolint // Cryptocurrency code - CS = NewCode("CS") - MAN = NewCode("MAN") - REM = NewCode("REM") - LYM = NewCode("LYM") - INSTAR = NewCode("INSTAR") - BFT = NewCode("BFT") - IHT = NewCode("IHT") - SENC = NewCode("SENC") - TOMO = NewCode("TOMO") - ELEC = NewCode("ELEC") - SHIP = NewCode("SHIP") - TFD = NewCode("TFD") - HAV = NewCode("HAV") - HUR = NewCode("HUR") - LST = NewCode("LST") - LINO = NewCode("LINO") - SWTH = NewCode("SWTH") - NKN = NewCode("NKN") - SOUL = NewCode("SOUL") - GALA_NEO = NewCode("GALA_NEO") // nolint // Cryptocurrency code - LRN = NewCode("LRN") - GSE = NewCode("GSE") - RATING = NewCode("RATING") - HSC = NewCode("HSC") - HIT = NewCode("HIT") - DX = NewCode("DX") - BXC = NewCode("BXC") - GARD = NewCode("GARD") - FTI = NewCode("FTI") - SOP = NewCode("SOP") - LEMO = NewCode("LEMO") - RED = NewCode("RED") - LBA = NewCode("LBA") - KAN = NewCode("KAN") - OPEN = NewCode("OPEN") - SKM = NewCode("SKM") - NBAI = NewCode("NBAI") - UPP = NewCode("UPP") - ATMI = NewCode("ATMI") - TMT = NewCode("TMT") - BBK = NewCode("BBK") - EDR = NewCode("EDR") - MET = NewCode("MET") - TCT = NewCode("TCT") - EXC = NewCode("EXC") - CNC = NewCode("CNC") - TIX = NewCode("TIX") - XTC = NewCode("XTC") - BU = NewCode("BU") - GNO = NewCode("GNO") - MLN = NewCode("MLN") - XBC = NewCode("XBC") - BTCD = NewCode("BTCD") - BURST = NewCode("BURST") - CLAM = NewCode("CLAM") - XCP = NewCode("XCP") - EMC2 = NewCode("EMC2") - EXP = NewCode("EXP") - FCT = NewCode("FCT") - GAME = NewCode("GAME") - GRC = NewCode("GRC") - HUC = NewCode("HUC") - LBC = NewCode("LBC") - NMC = NewCode("NMC") - NEOS = NewCode("NEOS") - OMNI = NewCode("OMNI") - PASC = NewCode("PASC") - PPC = NewCode("PPC") - DSH = NewCode("DSH") - GML = NewCode("GML") - GSY = NewCode("GSY") - POT = NewCode("POT") - XPM = NewCode("XPM") - AMP = NewCode("AMP") - VRC = NewCode("VRC") - VTC = NewCode("VTC") - ZERO07 = NewCode("007") - BIT16 = NewCode("BIT16") - TWO015 = NewCode("2015") - TWO56 = NewCode("256") - TWOBACCO = NewCode("2BACCO") - TWOGIVE = NewCode("2GIVE") - THIRTY2BIT = NewCode("32BIT") - THREE65 = NewCode("365") - FOUR04 = NewCode("404") - SEVEN00 = NewCode("700") - EIGHTBIT = NewCode("8BIT") - ACLR = NewCode("ACLR") - ACES = NewCode("ACES") - ACPR = NewCode("ACPR") - ACID = NewCode("ACID") - ACOIN = NewCode("ACOIN") - ACRN = NewCode("ACRN") - ADAM = NewCode("ADAM") - ADT = NewCode("ADT") - AIB = NewCode("AIB") - ADZ = NewCode("ADZ") - AECC = NewCode("AECC") - AM = NewCode("AM") - AGRI = NewCode("AGRI") - AGT = NewCode("AGT") - AIR = NewCode("AIR") - ALEX = NewCode("ALEX") - AUM = NewCode("AUM") - ALIEN = NewCode("ALIEN") - ALIS = NewCode("ALIS") - ALL = NewCode("ALL") - ASAFE = NewCode("ASAFE") - AMBER = NewCode("AMBER") - AMS = NewCode("AMS") - ANAL = NewCode("ANAL") - ACP = NewCode("ACP") - ANI = NewCode("ANI") - ANTI = NewCode("ANTI") - ALTC = NewCode("ALTC") - APT = NewCode("APT") - ARCO = NewCode("ARCO") - ALC = NewCode("ALC") - ARB = NewCode("ARB") - ARCT = NewCode("ARCT") - ARCX = NewCode("ARCX") - ARGUS = NewCode("ARGUS") - ARH = NewCode("ARH") - ARM = NewCode("ARM") - ARNA = NewCode("ARNA") - ARPA = NewCode("ARPA") - ARTA = NewCode("ARTA") - ABY = NewCode("ABY") - ARTC = NewCode("ARTC") - AL = NewCode("AL") - ASN = NewCode("ASN") - ADCN = NewCode("ADCN") - ATB = NewCode("ATB") - ATM = NewCode("ATM") - ATMCHA = NewCode("ATMCHA") - ATOM = NewCode("ATOM") - ADC = NewCode("ADC") - ARE = NewCode("ARE") - AUR = NewCode("AUR") - AV = NewCode("AV") - AXIOM = NewCode("AXIOM") - B2B = NewCode("B2B") - B2 = NewCode("B2") - B3 = NewCode("B3") - BAB = NewCode("BAB") - BAN = NewCode("BAN") - BamitCoin = NewCode("BamitCoin") - NANAS = NewCode("NANAS") - BBCC = NewCode("BBCC") - BTA = NewCode("BTA") - BSTK = NewCode("BSTK") - BATL = NewCode("BATL") - BBH = NewCode("BBH") - BITB = NewCode("BITB") - BRDD = NewCode("BRDD") - XBTS = NewCode("XBTS") - BVC = NewCode("BVC") - CHATX = NewCode("CHATX") - BEEP = NewCode("BEEP") - BEEZ = NewCode("BEEZ") - BENJI = NewCode("BENJI") - BERN = NewCode("BERN") - PROFIT = NewCode("PROFIT") - BEST = NewCode("BEST") - BGF = NewCode("BGF") - BIGUP = NewCode("BIGUP") - BLRY = NewCode("BLRY") - BILL = NewCode("BILL") - BIOB = NewCode("BIOB") - BIO = NewCode("BIO") - BIOS = NewCode("BIOS") - BPTN = NewCode("BPTN") - BTCA = NewCode("BTCA") - BA = NewCode("BA") - BAC = NewCode("BAC") - BBT = NewCode("BBT") - BOSS = NewCode("BOSS") - BRONZ = NewCode("BRONZ") - CAT = NewCode("CAT") - BTD = NewCode("BTD") - XBTC21 = NewCode("XBTC21") - BCA = NewCode("BCA") - BCP = NewCode("BCP") - BTDOLL = NewCode("BTDOLL") - LIZA = NewCode("LIZA") - BTCRED = NewCode("BTCRED") - BTCS = NewCode("BTCS") - BTU = NewCode("BTU") - BUM = NewCode("BUM") - LITE = NewCode("LITE") - BCM = NewCode("BCM") - BCS = NewCode("BCS") - BTCU = NewCode("BTCU") - BM = NewCode("BM") - BTCRY = NewCode("BTCRY") - BTCR = NewCode("BTCR") - HIRE = NewCode("HIRE") - STU = NewCode("STU") - BITOK = NewCode("BITOK") - BITON = NewCode("BITON") - BPC = NewCode("BPC") - BPOK = NewCode("BPOK") - BTP = NewCode("BTP") - BITCNY = NewCode("bitCNY") - RNTB = NewCode("RNTB") - BSH = NewCode("BSH") - XBS = NewCode("XBS") - BITS = NewCode("BITS") - BST = NewCode("BST") - BXT = NewCode("BXT") - VEG = NewCode("VEG") - VOLT = NewCode("VOLT") - BTV = NewCode("BTV") - BITZ = NewCode("BITZ") - BTZ = NewCode("BTZ") - BHC = NewCode("BHC") - BDC = NewCode("BDC") - JACK = NewCode("JACK") - BS = NewCode("BS") - BSTAR = NewCode("BSTAR") - BLAZR = NewCode("BLAZR") - BOD = NewCode("BOD") - BLUE = NewCode("BLUE") - BLU = NewCode("BLU") - BLUS = NewCode("BLUS") - BMT = NewCode("BMT") - BOLI = NewCode("BOLI") - BOMB = NewCode("BOMB") - BON = NewCode("BON") - BOOM = NewCode("BOOM") - BOSON = NewCode("BOSON") - BSC = NewCode("BSC") - BRH = NewCode("BRH") - BRAIN = NewCode("BRAIN") - BRE = NewCode("BRE") - BTCM = NewCode("BTCM") - BTCO = NewCode("BTCO") - TALK = NewCode("TALK") - BUB = NewCode("BUB") - BUY = NewCode("BUY") - BUZZ = NewCode("BUZZ") - BTH = NewCode("BTH") - C0C0 = NewCode("C0C0") - CAB = NewCode("CAB") - CF = NewCode("CF") - CLO = NewCode("CLO") - CAM = NewCode("CAM") - CD = NewCode("CD") - CANN = NewCode("CANN") - CNNC = NewCode("CNNC") - CPC = NewCode("CPC") - CST = NewCode("CST") - CAPT = NewCode("CAPT") - CARBON = NewCode("CARBON") - CME = NewCode("CME") - CTK = NewCode("CTK") - CBD = NewCode("CBD") - CCC = NewCode("CCC") - CNT = NewCode("CNT") - XCE = NewCode("XCE") - CHRG = NewCode("CHRG") - CHEMX = NewCode("CHEMX") - CHESS = NewCode("CHESS") - CKS = NewCode("CKS") - CHILL = NewCode("CHILL") - CHIP = NewCode("CHIP") - CHOOF = NewCode("CHOOF") - CRX = NewCode("CRX") - CIN = NewCode("CIN") - POLL = NewCode("POLL") - CLICK = NewCode("CLICK") - CLINT = NewCode("CLINT") - CLUB = NewCode("CLUB") - CLUD = NewCode("CLUD") - COX = NewCode("COX") - COXST = NewCode("COXST") - CFC = NewCode("CFC") - CTIC2 = NewCode("CTIC2") - COIN = NewCode("COIN") - BTTF = NewCode("BTTF") - C2 = NewCode("C2") - CAID = NewCode("CAID") - CL = NewCode("CL") - CTIC = NewCode("CTIC") - CXT = NewCode("CXT") - CHP = NewCode("CHP") - CV2 = NewCode("CV2") - COC = NewCode("COC") - COMP = NewCode("COMP") - CMS = NewCode("CMS") - CONX = NewCode("CONX") - CCX = NewCode("CCX") - CLR = NewCode("CLR") - CORAL = NewCode("CORAL") - CORG = NewCode("CORG") - CSMIC = NewCode("CSMIC") - CMC = NewCode("CMC") - COV = NewCode("COV") - COVX = NewCode("COVX") - CRAB = NewCode("CRAB") - CRAFT = NewCode("CRAFT") - CRNK = NewCode("CRNK") - CRAVE = NewCode("CRAVE") - CRM = NewCode("CRM") - XCRE = NewCode("XCRE") - CREDIT = NewCode("CREDIT") - CREVA = NewCode("CREVA") - CRIME = NewCode("CRIME") - CROC = NewCode("CROC") - CRC = NewCode("CRC") - CRW = NewCode("CRW") - CRY = NewCode("CRY") - CBX = NewCode("CBX") - TKTX = NewCode("TKTX") - CB = NewCode("CB") - CIRC = NewCode("CIRC") - CCB = NewCode("CCB") - CDO = NewCode("CDO") - CG = NewCode("CG") - CJ = NewCode("CJ") - CJC = NewCode("CJC") - CYT = NewCode("CYT") - CRPS = NewCode("CRPS") - PING = NewCode("PING") - CWXT = NewCode("CWXT") - CCT = NewCode("CCT") - CTL = NewCode("CTL") - CURVES = NewCode("CURVES") - CC = NewCode("CC") - CYC = NewCode("CYC") - CYG = NewCode("CYG") - CYP = NewCode("CYP") - FUNK = NewCode("FUNK") - CZECO = NewCode("CZECO") - DALC = NewCode("DALC") - DLISK = NewCode("DLISK") - MOOND = NewCode("MOOND") - DB = NewCode("DB") - DCC = NewCode("DCC") - DCYP = NewCode("DCYP") - DETH = NewCode("DETH") - DKC = NewCode("DKC") - DISK = NewCode("DISK") - DRKT = NewCode("DRKT") - DTT = NewCode("DTT") - DASHS = NewCode("DASHS") - DBTC = NewCode("DBTC") - DCT = NewCode("DCT") - DBET = NewCode("DBET") - DEC = NewCode("DEC") - DECR = NewCode("DECR") - DEA = NewCode("DEA") - DPAY = NewCode("DPAY") - DCRE = NewCode("DCRE") - DC = NewCode("DC") - DES = NewCode("DES") - DEM = NewCode("DEM") - DXC = NewCode("DXC") - DCK = NewCode("DCK") - CUBE = NewCode("CUBE") - DGMS = NewCode("DGMS") - DBG = NewCode("DBG") - DGCS = NewCode("DGCS") - DBLK = NewCode("DBLK") - DIME = NewCode("DIME") - DIRT = NewCode("DIRT") - DVD = NewCode("DVD") - DMT = NewCode("DMT") - NOTE = NewCode("NOTE") - DGORE = NewCode("DGORE") - DLC = NewCode("DLC") - DRT = NewCode("DRT") - DOTA = NewCode("DOTA") - DOX = NewCode("DOX") - DRA = NewCode("DRA") - DFT = NewCode("DFT") - XDB = NewCode("XDB") - DRM = NewCode("DRM") - DRZ = NewCode("DRZ") - DRACO = NewCode("DRACO") - DBIC = NewCode("DBIC") - DUB = NewCode("DUB") - GUM = NewCode("GUM") - DUR = NewCode("DUR") - DUST = NewCode("DUST") - DUX = NewCode("DUX") - DXO = NewCode("DXO") - ECN = NewCode("ECN") - EDR2 = NewCode("EDR2") - EA = NewCode("EA") - EAGS = NewCode("EAGS") - EMT = NewCode("EMT") - EBONUS = NewCode("EBONUS") - ECCHI = NewCode("ECCHI") - EKO = NewCode("EKO") - ECLI = NewCode("ECLI") - ECOB = NewCode("ECOB") - ECO = NewCode("ECO") - EDIT = NewCode("EDIT") - EDRC = NewCode("EDRC") - EDC = NewCode("EDC") - EGAME = NewCode("EGAME") - EGG = NewCode("EGG") - EGO = NewCode("EGO") - ELC = NewCode("ELC") - ELCO = NewCode("ELCO") - ECA = NewCode("ECA") - EPC = NewCode("EPC") - ELE = NewCode("ELE") - ONE337 = NewCode("1337") - EMB = NewCode("EMB") - EMC = NewCode("EMC") - EPY = NewCode("EPY") - EMPC = NewCode("EMPC") - EMP = NewCode("EMP") - ENE = NewCode("ENE") - EET = NewCode("EET") - XNG = NewCode("XNG") - EGMA = NewCode("EGMA") - ENTER = NewCode("ENTER") - ETRUST = NewCode("ETRUST") - EQL = NewCode("EQL") - EQM = NewCode("EQM") - EQT = NewCode("EQT") - ERR = NewCode("ERR") - ESC = NewCode("ESC") - ESP = NewCode("ESP") - ENT = NewCode("ENT") - ETCO = NewCode("ETCO") - DOGETH = NewCode("DOGETH") - ECASH = NewCode("ECASH") - ELITE = NewCode("ELITE") - ETHS = NewCode("ETHS") - ETL = NewCode("ETL") - ETZ = NewCode("ETZ") - EUC = NewCode("EUC") - EURC = NewCode("EURC") - EUROPE = NewCode("EUROPE") - EVA = NewCode("EVA") - EGC = NewCode("EGC") - EOC = NewCode("EOC") - EVIL = NewCode("EVIL") - EVO = NewCode("EVO") - EXB = NewCode("EXB") - EXIT = NewCode("EXIT") - XT = NewCode("XT") - F16 = NewCode("F16") - FADE = NewCode("FADE") - FAZZ = NewCode("FAZZ") - FX = NewCode("FX") - FIDEL = NewCode("FIDEL") - FIDGT = NewCode("FIDGT") - FIND = NewCode("FIND") - FPC = NewCode("FPC") - FIRE = NewCode("FIRE") - FFC = NewCode("FFC") - FRST = NewCode("FRST") - FIST = NewCode("FIST") - FIT = NewCode("FIT") - FLX = NewCode("FLX") - FLVR = NewCode("FLVR") - FLY = NewCode("FLY") - FONZ = NewCode("FONZ") - XFCX = NewCode("XFCX") - FOREX = NewCode("FOREX") - FRN = NewCode("FRN") - FRK = NewCode("FRK") - FRWC = NewCode("FRWC") - FGZ = NewCode("FGZ") - FRE = NewCode("FRE") - FRDC = NewCode("FRDC") - FJC = NewCode("FJC") - FURY = NewCode("FURY") - FSN = NewCode("FSN") - FCASH = NewCode("FCASH") - FTO = NewCode("FTO") - FUZZ = NewCode("FUZZ") - GAKH = NewCode("GAKH") - GBT = NewCode("GBT") - UNITS = NewCode("UNITS") - FOUR20G = NewCode("420G") - GENIUS = NewCode("GENIUS") - GEN = NewCode("GEN") - GEO = NewCode("GEO") - GER = NewCode("GER") - GSR = NewCode("GSR") - SPKTR = NewCode("SPKTR") - GIFT = NewCode("GIFT") - WTT = NewCode("WTT") - GHS = NewCode("GHS") - GIG = NewCode("GIG") - GOT = NewCode("GOT") - XGTC = NewCode("XGTC") - GIZ = NewCode("GIZ") - GLO = NewCode("GLO") - GCR = NewCode("GCR") - BSTY = NewCode("BSTY") - GLC = NewCode("GLC") - GSX = NewCode("GSX") - GOAT = NewCode("GOAT") - GB = NewCode("GB") - GFL = NewCode("GFL") - MNTP = NewCode("MNTP") - GP = NewCode("GP") - GLUCK = NewCode("GLUCK") - GOON = NewCode("GOON") - GTFO = NewCode("GTFO") - GOTX = NewCode("GOTX") - GPU = NewCode("GPU") - GRF = NewCode("GRF") - GRAM = NewCode("GRAM") - GRAV = NewCode("GRAV") - GBIT = NewCode("GBIT") - GREED = NewCode("GREED") - GE = NewCode("GE") - GREENF = NewCode("GREENF") - GRE = NewCode("GRE") - GREXIT = NewCode("GREXIT") - GMCX = NewCode("GMCX") - GROW = NewCode("GROW") - GSM = NewCode("GSM") - GT = NewCode("GT") - NLG = NewCode("NLG") - HKN = NewCode("HKN") - HAC = NewCode("HAC") - HALLO = NewCode("HALLO") - HAMS = NewCode("HAMS") - HPC = NewCode("HPC") - HAWK = NewCode("HAWK") - HAZE = NewCode("HAZE") - HZT = NewCode("HZT") - HDG = NewCode("HDG") - HEDG = NewCode("HEDG") - HEEL = NewCode("HEEL") - HMP = NewCode("HMP") - PLAY = NewCode("PLAY") - HXX = NewCode("HXX") - XHI = NewCode("XHI") - HVCO = NewCode("HVCO") - HTC = NewCode("HTC") - MINH = NewCode("MINH") - HODL = NewCode("HODL") - HON = NewCode("HON") - HOPE = NewCode("HOPE") - HQX = NewCode("HQX") - HSP = NewCode("HSP") - HTML5 = NewCode("HTML5") - HYPERX = NewCode("HYPERX") - HPS = NewCode("HPS") - IOC = NewCode("IOC") - IBANK = NewCode("IBANK") - IBITS = NewCode("IBITS") - ICASH = NewCode("ICASH") - ICOB = NewCode("ICOB") - ICON = NewCode("ICON") - IETH = NewCode("IETH") - ILM = NewCode("ILM") - IMPS = NewCode("IMPS") - NKA = NewCode("NKA") - INCP = NewCode("INCP") - IN = NewCode("IN") - INC = NewCode("INC") - IMS = NewCode("IMS") - IFLT = NewCode("IFLT") - INFX = NewCode("INFX") - INGT = NewCode("INGT") - INPAY = NewCode("INPAY") - INSANE = NewCode("INSANE") - INXT = NewCode("INXT") - IFT = NewCode("IFT") - INV = NewCode("INV") - IVZ = NewCode("IVZ") - ILT = NewCode("ILT") - IONX = NewCode("IONX") - ISL = NewCode("ISL") - ITI = NewCode("ITI") - ING = NewCode("ING") - IEC = NewCode("IEC") - IW = NewCode("IW") - IXC = NewCode("IXC") - IXT = NewCode("IXT") - JPC = NewCode("JPC") - JANE = NewCode("JANE") - JWL = NewCode("JWL") - JIF = NewCode("JIF") - JOBS = NewCode("JOBS") - JOCKER = NewCode("JOCKER") - JW = NewCode("JW") - JOK = NewCode("JOK") - XJO = NewCode("XJO") - KGB = NewCode("KGB") - KARMC = NewCode("KARMC") - KARMA = NewCode("KARMA") - KASHH = NewCode("KASHH") - KAT = NewCode("KAT") - KC = NewCode("KC") - KIDS = NewCode("KIDS") - KIN = NewCode("KIN") - KISS = NewCode("KISS") - KOBO = NewCode("KOBO") - TP1 = NewCode("TP1") - KRAK = NewCode("KRAK") - KGC = NewCode("KGC") - KTK = NewCode("KTK") - KR = NewCode("KR") - KUBO = NewCode("KUBO") - KURT = NewCode("KURT") - KUSH = NewCode("KUSH") - LANA = NewCode("LANA") - LTH = NewCode("LTH") - LAZ = NewCode("LAZ") - LEA = NewCode("LEA") - LEAF = NewCode("LEAF") - LENIN = NewCode("LENIN") - LEPEN = NewCode("LEPEN") - LIR = NewCode("LIR") - LVG = NewCode("LVG") - LGBTQ = NewCode("LGBTQ") - LHC = NewCode("LHC") - EXT = NewCode("EXT") - LBTC = NewCode("LBTC") - LSD = NewCode("LSD") - LIMX = NewCode("LIMX") - LTD = NewCode("LTD") - LINDA = NewCode("LINDA") - LKC = NewCode("LKC") - LBTCX = NewCode("LBTCX") - LCC = NewCode("LCC") - LTCU = NewCode("LTCU") - LTCR = NewCode("LTCR") - LDOGE = NewCode("LDOGE") - LTS = NewCode("LTS") - LIV = NewCode("LIV") - LIZI = NewCode("LIZI") - LOC = NewCode("LOC") - LOCX = NewCode("LOCX") - LOOK = NewCode("LOOK") - LOOT = NewCode("LOOT") - XLTCG = NewCode("XLTCG") - BASH = NewCode("BASH") - LUCKY = NewCode("LUCKY") - L7S = NewCode("L7S") - LDM = NewCode("LDM") - LUMI = NewCode("LUMI") - LUNA = NewCode("LUNA") - LC = NewCode("LC") - LUX = NewCode("LUX") - MCRN = NewCode("MCRN") - XMG = NewCode("XMG") - MMXIV = NewCode("MMXIV") - MAT = NewCode("MAT") - MAO = NewCode("MAO") - MAPC = NewCode("MAPC") - MRB = NewCode("MRB") - MXT = NewCode("MXT") - MARV = NewCode("MARV") - MARX = NewCode("MARX") - MCAR = NewCode("MCAR") - MM = NewCode("MM") - MVC = NewCode("MVC") - MAVRO = NewCode("MAVRO") - MAX = NewCode("MAX") - MAZE = NewCode("MAZE") - MBIT = NewCode("MBIT") - MCOIN = NewCode("MCOIN") - MPRO = NewCode("MPRO") - XMS = NewCode("XMS") - MLITE = NewCode("MLITE") - MLNC = NewCode("MLNC") - MENTAL = NewCode("MENTAL") - MERGEC = NewCode("MERGEC") - MTLMC3 = NewCode("MTLMC3") - METAL = NewCode("METAL") - MUU = NewCode("MUU") - MILO = NewCode("MILO") - MND = NewCode("MND") - XMINE = NewCode("XMINE") - MNM = NewCode("MNM") - XNM = NewCode("XNM") - MIRO = NewCode("MIRO") - MIS = NewCode("MIS") - MMXVI = NewCode("MMXVI") - MOIN = NewCode("MOIN") - MOJO = NewCode("MOJO") - TAB = NewCode("TAB") - MONETA = NewCode("MONETA") - MUE = NewCode("MUE") - MONEY = NewCode("MONEY") - MRP = NewCode("MRP") - MOTO = NewCode("MOTO") - MULTI = NewCode("MULTI") - MST = NewCode("MST") - MVR = NewCode("MVR") - MYSTIC = NewCode("MYSTIC") - WISH = NewCode("WISH") - NKT = NewCode("NKT") - NAT = NewCode("NAT") - ENAU = NewCode("ENAU") - NEBU = NewCode("NEBU") - NEF = NewCode("NEF") - NBIT = NewCode("NBIT") - NETKO = NewCode("NETKO") - NTM = NewCode("NTM") - NETC = NewCode("NETC") - NRC = NewCode("NRC") - NTK = NewCode("NTK") - NTRN = NewCode("NTRN") - NEVA = NewCode("NEVA") - NIC = NewCode("NIC") - NKC = NewCode("NKC") - NYC = NewCode("NYC") - NZC = NewCode("NZC") - NICE = NewCode("NICE") - NDOGE = NewCode("NDOGE") - XTR = NewCode("XTR") - N2O = NewCode("N2O") - NIXON = NewCode("NIXON") - NOC = NewCode("NOC") - NODC = NewCode("NODC") - NODES = NewCode("NODES") - NODX = NewCode("NODX") - NLC = NewCode("NLC") - NLC2 = NewCode("NLC2") - NOO = NewCode("NOO") - NVC = NewCode("NVC") - NPC = NewCode("NPC") - NUBIS = NewCode("NUBIS") - NUKE = NewCode("NUKE") - N7 = NewCode("N7") - NUM = NewCode("NUM") - NMR = NewCode("NMR") - NXE = NewCode("NXE") - OBS = NewCode("OBS") - OCEAN = NewCode("OCEAN") - OCOW = NewCode("OCOW") - EIGHT88 = NewCode("888") - OCC = NewCode("OCC") - OK = NewCode("OK") - ODNT = NewCode("ODNT") - FLAV = NewCode("FLAV") - OLIT = NewCode("OLIT") - OLYMP = NewCode("OLYMP") - OMA = NewCode("OMA") - OMC = NewCode("OMC") - ONEK = NewCode("ONEK") - ONX = NewCode("ONX") - XPO = NewCode("XPO") - OPAL = NewCode("OPAL") - OTN = NewCode("OTN") - OP = NewCode("OP") - OPES = NewCode("OPES") - OPTION = NewCode("OPTION") - ORLY = NewCode("ORLY") - OS76 = NewCode("OS76") - OZC = NewCode("OZC") - P7C = NewCode("P7C") - PAC = NewCode("PAC") - PAK = NewCode("PAK") - PAL = NewCode("PAL") - PND = NewCode("PND") - PINKX = NewCode("PINKX") - POPPY = NewCode("POPPY") - DUO = NewCode("DUO") - PARA = NewCode("PARA") - PKB = NewCode("PKB") - GENE = NewCode("GENE") - PARTY = NewCode("PARTY") - PYN = NewCode("PYN") - XPY = NewCode("XPY") - CON = NewCode("CON") - PAYP = NewCode("PAYP") - GUESS = NewCode("GUESS") - PEN = NewCode("PEN") - PTA = NewCode("PTA") - PEO = NewCode("PEO") - PSB = NewCode("PSB") - XPD = NewCode("XPD") - PXL = NewCode("PXL") - PHR = NewCode("PHR") - PIE = NewCode("PIE") - PIO = NewCode("PIO") - PIPR = NewCode("PIPR") - SKULL = NewCode("SKULL") - PLANET = NewCode("PLANET") - PNC = NewCode("PNC") - XPTX = NewCode("XPTX") - PLNC = NewCode("PLNC") - XPS = NewCode("XPS") - POKE = NewCode("POKE") - PLBT = NewCode("PLBT") - POM = NewCode("POM") - PONZ2 = NewCode("PONZ2") - PONZI = NewCode("PONZI") - XSP = NewCode("XSP") - XPC = NewCode("XPC") - PEX = NewCode("PEX") - TRON = NewCode("TRON") - POST = NewCode("POST") - POSW = NewCode("POSW") - PWR = NewCode("PWR") - POWER = NewCode("POWER") - PRE = NewCode("PRE") - PRS = NewCode("PRS") - PXI = NewCode("PXI") - PEXT = NewCode("PEXT") - PRIMU = NewCode("PRIMU") - PRX = NewCode("PRX") - PRM = NewCode("PRM") - PRIX = NewCode("PRIX") - XPRO = NewCode("XPRO") - PCM = NewCode("PCM") - PROC = NewCode("PROC") - NANOX = NewCode("NANOX") - VRP = NewCode("VRP") - PTY = NewCode("PTY") - PSI = NewCode("PSI") - PSY = NewCode("PSY") - PULSE = NewCode("PULSE") - PUPA = NewCode("PUPA") - PURE = NewCode("PURE") - VIDZ = NewCode("VIDZ") - PUTIN = NewCode("PUTIN") - PX = NewCode("PX") - QTM = NewCode("QTM") - QTZ = NewCode("QTZ") - QBC = NewCode("QBC") - XQN = NewCode("XQN") - RBBT = NewCode("RBBT") - RAC = NewCode("RAC") - RADI = NewCode("RADI") - RAD = NewCode("RAD") - RAI = NewCode("RAI") - XRA = NewCode("XRA") - RATIO = NewCode("RATIO") - REA = NewCode("REA") - RCX = NewCode("RCX") - REE = NewCode("REE") - REC = NewCode("REC") - RMS = NewCode("RMS") - RBIT = NewCode("RBIT") - RNC = NewCode("RNC") - REV = NewCode("REV") - RH = NewCode("RH") - XRL = NewCode("XRL") - RICE = NewCode("RICE") - RICHX = NewCode("RICHX") - RID = NewCode("RID") - RIDE = NewCode("RIDE") - RBT = NewCode("RBT") - RING = NewCode("RING") - RIO = NewCode("RIO") - RISE = NewCode("RISE") - ROCKET = NewCode("ROCKET") - RPC = NewCode("RPC") - ROS = NewCode("ROS") - ROYAL = NewCode("ROYAL") - RSGP = NewCode("RSGP") - RBIES = NewCode("RBIES") - RUBIT = NewCode("RUBIT") - RBY = NewCode("RBY") - RUC = NewCode("RUC") - RUPX = NewCode("RUPX") - RUP = NewCode("RUP") - RUST = NewCode("RUST") - SFE = NewCode("SFE") - SLS = NewCode("SLS") - SMSR = NewCode("SMSR") - RONIN = NewCode("RONIN") - STV = NewCode("STV") - HIFUN = NewCode("HIFUN") - MAD = NewCode("MAD") - SANDG = NewCode("SANDG") - STO = NewCode("STO") - SCAN = NewCode("SCAN") - SCITW = NewCode("SCITW") - SCRPT = NewCode("SCRPT") - SCRT = NewCode("SCRT") - SED = NewCode("SED") - SEEDS = NewCode("SEEDS") - B2X = NewCode("B2X") - SEL = NewCode("SEL") - SLFI = NewCode("SLFI") - SMBR = NewCode("SMBR") - SEN = NewCode("SEN") - SENT = NewCode("SENT") - SRNT = NewCode("SRNT") - SEV = NewCode("SEV") - SP = NewCode("SP") - SXC = NewCode("SXC") - GELD = NewCode("GELD") - SHDW = NewCode("SHDW") - SDC = NewCode("SDC") - SAK = NewCode("SAK") - SHRP = NewCode("SHRP") - SHELL = NewCode("SHELL") - SH = NewCode("SH") - SHORTY = NewCode("SHORTY") - SHREK = NewCode("SHREK") - SHRM = NewCode("SHRM") - SIB = NewCode("SIB") - SIGT = NewCode("SIGT") - SLCO = NewCode("SLCO") - SIGU = NewCode("SIGU") - SIX = NewCode("SIX") - SJW = NewCode("SJW") - SKB = NewCode("SKB") - SW = NewCode("SW") - SLEEP = NewCode("SLEEP") - SLING = NewCode("SLING") - SMART = NewCode("SMART") - SMC = NewCode("SMC") - SMF = NewCode("SMF") - SOCC = NewCode("SOCC") - SCL = NewCode("SCL") - SDAO = NewCode("SDAO") - SOLAR = NewCode("SOLAR") - SOLO = NewCode("SOLO") - SCT = NewCode("SCT") - SONG = NewCode("SONG") - ALTCOM = NewCode("ALTCOM") - SPHTX = NewCode("SPHTX") - SPC = NewCode("SPC") - SPACE = NewCode("SPACE") - SBT = NewCode("SBT") - SPEC = NewCode("SPEC") - SPX = NewCode("SPX") - SCS = NewCode("SCS") - SPORT = NewCode("SPORT") - SPT = NewCode("SPT") - SPR = NewCode("SPR") - SPEX = NewCode("SPEX") - SQL = NewCode("SQL") - SBIT = NewCode("SBIT") - STHR = NewCode("STHR") - STALIN = NewCode("STALIN") - STAR = NewCode("STAR") - STA = NewCode("STA") - START = NewCode("START") - STP = NewCode("STP") - PNK = NewCode("PNK") - STEPS = NewCode("STEPS") - STK = NewCode("STK") - STONK = NewCode("STONK") - STS = NewCode("STS") - STRP = NewCode("STRP") - STY = NewCode("STY") - XMT = NewCode("XMT") - SSTC = NewCode("SSTC") - SUPER = NewCode("SUPER") - SRND = NewCode("SRND") - STRB = NewCode("STRB") - M1 = NewCode("M1") - SPM = NewCode("SPM") - BUCKS = NewCode("BUCKS") - TOKEN = NewCode("TOKEN") - SWT = NewCode("SWT") - SWEET = NewCode("SWEET") - SWING = NewCode("SWING") - CHSB = NewCode("CHSB") - SIC = NewCode("SIC") - SDP = NewCode("SDP") - XSY = NewCode("XSY") - SYNX = NewCode("SYNX") - SNRG = NewCode("SNRG") - TAG = NewCode("TAG") - TAGR = NewCode("TAGR") - TAJ = NewCode("TAJ") - TAK = NewCode("TAK") - TAKE = NewCode("TAKE") - TAM = NewCode("TAM") - XTO = NewCode("XTO") - TAP = NewCode("TAP") - TLE = NewCode("TLE") - TSE = NewCode("TSE") - TLEX = NewCode("TLEX") - TAXI = NewCode("TAXI") - TCN = NewCode("TCN") - TDFB = NewCode("TDFB") - TEAM = NewCode("TEAM") - TECH = NewCode("TECH") - TEC = NewCode("TEC") - TEK = NewCode("TEK") - TB = NewCode("TB") - TLX = NewCode("TLX") - TELL = NewCode("TELL") - TENNET = NewCode("TENNET") - TES = NewCode("TES") - TGS = NewCode("TGS") - XVE = NewCode("XVE") - TCR = NewCode("TCR") - GCC = NewCode("GCC") - MAY = NewCode("MAY") - THOM = NewCode("THOM") - TIA = NewCode("TIA") - TIDE = NewCode("TIDE") - TIE = NewCode("TIE") - TIT = NewCode("TIT") - TTC = NewCode("TTC") - TODAY = NewCode("TODAY") - TBX = NewCode("TBX") - TDS = NewCode("TDS") - TLOSH = NewCode("TLOSH") - TOKC = NewCode("TOKC") - TMRW = NewCode("TMRW") - TOOL = NewCode("TOOL") - TCX = NewCode("TCX") - TOT = NewCode("TOT") - TX = NewCode("TX") - TRANSF = NewCode("TRANSF") - TRAP = NewCode("TRAP") - TBCX = NewCode("TBCX") - TRICK = NewCode("TRICK") - TPG = NewCode("TPG") - TFL = NewCode("TFL") - TRUMP = NewCode("TRUMP") - TNG = NewCode("TNG") - TUR = NewCode("TUR") - TWERK = NewCode("TWERK") - TWIST = NewCode("TWIST") - TWO = NewCode("TWO") - UCASH = NewCode("UCASH") - UAE = NewCode("UAE") - XBU = NewCode("XBU") - UBQ = NewCode("UBQ") - U = NewCode("U") - UDOWN = NewCode("UDOWN") - GAIN = NewCode("GAIN") - USC = NewCode("USC") - UMC = NewCode("UMC") - UNF = NewCode("UNF") - UNIFY = NewCode("UNIFY") - USDE = NewCode("USDE") - UBTC = NewCode("UBTC") - UIS = NewCode("UIS") - UNIT = NewCode("UNIT") - UNI = NewCode("UNI") - UXC = NewCode("UXC") - URC = NewCode("URC") - XUP = NewCode("XUP") - UFR = NewCode("UFR") - URO = NewCode("URO") - UTLE = NewCode("UTLE") - VAL = NewCode("VAL") - VPRC = NewCode("VPRC") - VAPOR = NewCode("VAPOR") - VCOIN = NewCode("VCOIN") - VEC = NewCode("VEC") - VEC2 = NewCode("VEC2") - VLT = NewCode("VLT") - VENE = NewCode("VENE") - VNTX = NewCode("VNTX") - VTN = NewCode("VTN") - CRED = NewCode("CRED") - VERS = NewCode("VERS") - VTX = NewCode("VTX") - VTY = NewCode("VTY") - VIP = NewCode("VIP") - VISIO = NewCode("VISIO") - VK = NewCode("VK") - VOL = NewCode("VOL") - VOYA = NewCode("VOYA") - VPN = NewCode("VPN") - XVS = NewCode("XVS") - VTL = NewCode("VTL") - VULC = NewCode("VULC") - VVI = NewCode("VVI") - WGR = NewCode("WGR") - WAM = NewCode("WAM") - WARP = NewCode("WARP") - WASH = NewCode("WASH") - WGO = NewCode("WGO") - WAY = NewCode("WAY") - WCASH = NewCode("WCASH") - WEALTH = NewCode("WEALTH") - WEEK = NewCode("WEEK") - WHO = NewCode("WHO") - WIC = NewCode("WIC") - WBB = NewCode("WBB") - WINE = NewCode("WINE") - WINK = NewCode("WINK") - WISC = NewCode("WISC") - WITCH = NewCode("WITCH") - WMC = NewCode("WMC") - WOMEN = NewCode("WOMEN") - WOK = NewCode("WOK") - WRT = NewCode("WRT") - XCO = NewCode("XCO") - X2 = NewCode("X2") - XNX = NewCode("XNX") - XAU = NewCode("XAU") - XAV = NewCode("XAV") - XDE2 = NewCode("XDE2") - XDE = NewCode("XDE") - XIOS = NewCode("XIOS") - XOC = NewCode("XOC") - XSSX = NewCode("XSSX") - XBY = NewCode("XBY") - YAC = NewCode("YAC") - YMC = NewCode("YMC") - YAY = NewCode("YAY") - YBC = NewCode("YBC") - YES = NewCode("YES") - YOB2X = NewCode("YOB2X") - YOVI = NewCode("YOVI") - ZYD = NewCode("ZYD") - ZECD = NewCode("ZECD") - ZEIT = NewCode("ZEIT") - ZENI = NewCode("ZENI") - ZET2 = NewCode("ZET2") - ZET = NewCode("ZET") - ZMC = NewCode("ZMC") - ZIRK = NewCode("ZIRK") - ZLQ = NewCode("ZLQ") - ZNE = NewCode("ZNE") - ZONTO = NewCode("ZONTO") - ZOOM = NewCode("ZOOM") - ZRC = NewCode("ZRC") - ZUR = NewCode("ZUR") - ZB = NewCode("ZB") - QC = NewCode("QC") - HLC = NewCode("HLC") - SAFE = NewCode("SAFE") - BTN = NewCode("BTN") - CDC = NewCode("CDC") - DDM = NewCode("DDM") - HOTC = NewCode("HOTC") - BDS = NewCode("BDS") - AAA = NewCode("AAA") - XWC = NewCode("XWC") - PDX = NewCode("PDX") - SLT = NewCode("SLT") - HPY = NewCode("HPY") - XXRP = NewCode("XXRP") // XRP - XXBT = NewCode("XXBT") // BTC, but XXBT instead - XXDG = NewCode("XXDG") // DOGE - XDG = NewCode("XDG") // DOGE - HKD = NewCode("HKD") // Hong Kong Dollar - AUD = NewCode("AUD") // Australian Dollar - USD = NewCode("USD") // United States Dollar - ZUSD = NewCode("ZUSD") // United States Dollar, but with a Z in front of it - EUR = NewCode("EUR") // Euro - ZEUR = NewCode("ZEUR") // Euro, but with a Z in front of it - CAD = NewCode("CAD") // Canadaian Dollar - ZCAD = NewCode("ZCAD") // Canadaian Dollar, but with a Z in front of it - SGD = NewCode("SGD") // Singapore Dollar - RUB = NewCode("RUB") // RUssian ruBle - RUR = NewCode("RUR") // RUssian Ruble - PLN = NewCode("PLN") // Polish zÅ‚oty - TRY = NewCode("TRY") // Turkish lira - UAH = NewCode("UAH") // Ukrainian hryvnia - JPY = NewCode("JPY") // Japanese yen - ZJPY = NewCode("ZJPY") // Japanese yen, but with a Z in front of it - LCH = NewCode("LCH") - MYR = NewCode("MYR") - AFN = NewCode("AFN") - ARS = NewCode("ARS") - AWG = NewCode("AWG") - AZN = NewCode("AZN") - BSD = NewCode("BSD") - BBD = NewCode("BBD") - BYN = NewCode("BYN") - BZD = NewCode("BZD") - BMD = NewCode("BMD") - BOB = NewCode("BOB") - BAM = NewCode("BAM") - BWP = NewCode("BWP") - BGN = NewCode("BGN") - BRL = NewCode("BRL") - BND = NewCode("BND") - KHR = NewCode("KHR") - KYD = NewCode("KYD") - CLP = NewCode("CLP") - CNY = NewCode("CNY") - COP = NewCode("COP") - HRK = NewCode("HRK") - CUP = NewCode("CUP") - CZK = NewCode("CZK") - DKK = NewCode("DKK") - DOP = NewCode("DOP") - XCD = NewCode("XCD") - EGP = NewCode("EGP") - SVC = NewCode("SVC") - FKP = NewCode("FKP") - FJD = NewCode("FJD") - GIP = NewCode("GIP") - GTQ = NewCode("GTQ") - GGP = NewCode("GGP") - GYD = NewCode("GYD") - HNL = NewCode("HNL") - HUF = NewCode("HUF") - ISK = NewCode("ISK") - INR = NewCode("INR") - IDR = NewCode("IDR") - IRR = NewCode("IRR") - IMP = NewCode("IMP") - ILS = NewCode("ILS") - JMD = NewCode("JMD") - JEP = NewCode("JEP") - KZT = NewCode("KZT") - KPW = NewCode("KPW") - KGS = NewCode("KGS") - LAK = NewCode("LAK") - LBP = NewCode("LBP") - LRD = NewCode("LRD") - MKD = NewCode("MKD") - MUR = NewCode("MUR") - MXN = NewCode("MXN") - MNT = NewCode("MNT") - MZN = NewCode("MZN") - NAD = NewCode("NAD") - NPR = NewCode("NPR") - ANG = NewCode("ANG") - NZD = NewCode("NZD") - NIO = NewCode("NIO") - NGN = NewCode("NGN") - NOK = NewCode("NOK") - OMR = NewCode("OMR") - PKR = NewCode("PKR") - PAB = NewCode("PAB") - PYG = NewCode("PYG") - PHP = NewCode("PHP") - QAR = NewCode("QAR") - RON = NewCode("RON") - SHP = NewCode("SHP") - SAR = NewCode("SAR") - RSD = NewCode("RSD") - SCR = NewCode("SCR") - SOS = NewCode("SOS") - ZAR = NewCode("ZAR") - LKR = NewCode("LKR") - SEK = NewCode("SEK") - CHF = NewCode("CHF") - SRD = NewCode("SRD") - SYP = NewCode("SYP") - TWD = NewCode("TWD") - THB = NewCode("THB") - TTD = NewCode("TTD") - TVD = NewCode("TVD") - GBP = NewCode("GBP") - UYU = NewCode("UYU") - UZS = NewCode("UZS") - VEF = NewCode("VEF") - VND = NewCode("VND") - YER = NewCode("YER") - ZWD = NewCode("ZWD") - XETH = NewCode("XETH") - FX_BTC = NewCode("FX_BTC") // nolint // Cryptocurrency code - AAVE = NewCode("AAVE") - YFI = NewCode("YFI") - BAL = NewCode("BAL") - UMA = NewCode("UMA") - SNX = NewCode("SNX") - CRV = NewCode("CRV") - OXT = NewCode("OXT") - BUSD = NewCode("BUSD") - SRM = NewCode("SRM") - FTT = NewCode("FTT") - UST = NewCode("UST") + BTC = NewCode("BTC") + LTC = NewCode("LTC") + ETH = NewCode("ETH") + XRP = NewCode("XRP") + BCH = NewCode("BCH") + EOS = NewCode("EOS") + XLM = NewCode("XLM") + USDT = NewCode("USDT") + USDC = NewCode("USDC") + ADA = NewCode("ADA") + XMR = NewCode("XMR") + TRX = NewCode("TRX") + MIOTA = NewCode("MIOTA") + DASH = NewCode("DASH") + BNB = NewCode("BNB") + NEO = NewCode("NEO") + ETC = NewCode("ETC") + XEM = NewCode("XEM") + XTZ = NewCode("XTZ") + VET = NewCode("VET") + DOGE = NewCode("DOGE") + ZEC = NewCode("ZEC") + OMG = NewCode("OMG") + BTG = NewCode("BTG") + MKR = NewCode("MKR") + BCN = NewCode("BCN") + ONT = NewCode("ONT") + ZRX = NewCode("ZRX") + LSK = NewCode("LSK") + DCR = NewCode("DCR") + QTUM = NewCode("QTUM") + BCD = NewCode("BCD") + BTS = NewCode("BTS") + NANO = NewCode("NANO") + ZIL = NewCode("ZIL") + SC = NewCode("SC") + DGB = NewCode("DGB") + ICX = NewCode("ICX") + STEEM = NewCode("STEEM") + AE = NewCode("AE") + XVG = NewCode("XVG") + WAVES = NewCode("WAVES") + NPXS = NewCode("NPXS") + ETN = NewCode("ETN") + BTM = NewCode("BTM") + BAT = NewCode("BAT") + ETP = NewCode("ETP") + HOT = NewCode("HOT") + STRAT = NewCode("STRAT") // nolint // Cryptocurrency code + GNT = NewCode("GNT") + REP = NewCode("REP") + SNT = NewCode("SNT") + PPT = NewCode("PPT") + KMD = NewCode("KMD") + TUSD = NewCode("TUSD") + CNX = NewCode("CNX") + LINK = NewCode("LINK") + WTC = NewCode("WTC") + ARDR = NewCode("ARDR") + WAN = NewCode("WAN") + MITH = NewCode("MITH") + RDD = NewCode("RDD") + IOST = NewCode("IOST") + IOT = NewCode("IOT") + KCS = NewCode("KCS") + MAID = NewCode("MAID") + XET = NewCode("XET") + MOAC = NewCode("MOAC") + HC = NewCode("HC") + AION = NewCode("AION") + AOA = NewCode("AOA") + HT = NewCode("HT") + ELF = NewCode("ELF") + LRC = NewCode("LRC") + BNT = NewCode("BNT") + CMT = NewCode("CMT") + DGD = NewCode("DGD") + DCN = NewCode("DCN") + FUN = NewCode("FUN") + GXS = NewCode("GXS") + DROP = NewCode("DROP") + MANA = NewCode("MANA") + PAY = NewCode("PAY") + MCO = NewCode("MCO") + THETA = NewCode("THETA") + NXT = NewCode("NXT") + NOAH = NewCode("NOAH") + LOOM = NewCode("LOOM") + POWR = NewCode("POWR") + WAX = NewCode("WAX") + ELA = NewCode("ELA") + PIVX = NewCode("PIVX") + XIN = NewCode("XIN") + DAI = NewCode("DAI") + BTCP = NewCode("BTCP") + NEXO = NewCode("NEXO") + XBT = NewCode("XBT") + SAN = NewCode("SAN") + GAS = NewCode("GAS") + BCC = NewCode("BCC") + HCC = NewCode("HCC") + OAX = NewCode("OAX") + DNT = NewCode("DNT") + ICN = NewCode("ICN") + LLT = NewCode("LLT") + YOYO = NewCode("YOYO") + SNGLS = NewCode("SNGLS") + BQX = NewCode("BQX") + KNC = NewCode("KNC") + SNM = NewCode("SNM") + CTR = NewCode("CTR") + SALT = NewCode("SALT") + MDA = NewCode("MDA") + IOTA = NewCode("IOTA") + SUB = NewCode("SUB") + MTL = NewCode("MTL") + MTH = NewCode("MTH") + ENG = NewCode("ENG") + AST = NewCode("AST") + CLN = NewCode("CLN") + EDG = NewCode("EDG") + FIRST = NewCode("1ST") + GOLOS = NewCode("GOLOS") + ANT = NewCode("ANT") + GBG = NewCode("GBG") + HMQ = NewCode("HMQ") + INCNT = NewCode("INCNT") + ACE = NewCode("ACE") + ACT = NewCode("ACT") + AAC = NewCode("AAC") + AIDOC = NewCode("AIDOC") + SOC = NewCode("SOC") + ATL = NewCode("ATL") + AVT = NewCode("AVT") + BKX = NewCode("BKX") + BEC = NewCode("BEC") + VEE = NewCode("VEE") + PTOY = NewCode("PTOY") + CAG = NewCode("CAG") + CIC = NewCode("CIC") + CBT = NewCode("CBT") + CAN = NewCode("CAN") + DAT = NewCode("DAT") + DNA = NewCode("DNA") + INT = NewCode("INT") + IPC = NewCode("IPC") + ILA = NewCode("ILA") + LIGHT = NewCode("LIGHT") + MAG = NewCode("MAG") + AMM = NewCode("AMM") + MOF = NewCode("MOF") + MGC = NewCode("MGC") + OF = NewCode("OF") + LA = NewCode("LA") + LEV = NewCode("LEV") + NGC = NewCode("NGC") + OKB = NewCode("OKB") + MOT = NewCode("MOT") + PRA = NewCode("PRA") + R = NewCode("R") + SSC = NewCode("SSC") + SHOW = NewCode("SHOW") + SPF = NewCode("SPF") + SNC = NewCode("SNC") + SWFTC = NewCode("SWFTC") + TRA = NewCode("TRA") + TOPC = NewCode("TOPC") + TRIO = NewCode("TRIO") + QVT = NewCode("QVT") + UCT = NewCode("UCT") + UKG = NewCode("UKG") + UTK = NewCode("UTK") + VIU = NewCode("VIU") + WFEE = NewCode("WFEE") + WRC = NewCode("WRC") + UGC = NewCode("UGC") + YEE = NewCode("YEE") + YOYOW = NewCode("YOYOW") + ZIP = NewCode("ZIP") + READ = NewCode("READ") + RCT = NewCode("RCT") + REF = NewCode("REF") + XUC = NewCode("XUC") + FAIR = NewCode("FAIR") + GSC = NewCode("GSC") + HMC = NewCode("HMC") + PLU = NewCode("PLU") + PRO = NewCode("PRO") + QRL = NewCode("QRL") + REN = NewCode("REN") + ROUND = NewCode("ROUND") + SRN = NewCode("SRN") + XID = NewCode("XID") + SBD = NewCode("SBD") + TAAS = NewCode("TAAS") + TKN = NewCode("TKN") + VEN = NewCode("VEN") + VSL = NewCode("VSL") + TRST = NewCode("TRST") + XXX = NewCode("XXX") + IND = NewCode("IND") + LDC = NewCode("LDC") + GUP = NewCode("GUP") + MGO = NewCode("MGO") + MYST = NewCode("MYST") + NEU = NewCode("NEU") + NET = NewCode("NET") + BMC = NewCode("BMC") + BCAP = NewCode("BCAP") + TIME = NewCode("TIME") + CFI = NewCode("CFI") + EVX = NewCode("EVX") + REQ = NewCode("REQ") + VIB = NewCode("VIB") + ARK = NewCode("ARK") + MOD = NewCode("MOD") + ENJ = NewCode("ENJ") + STORJ = NewCode("STORJ") + RCN = NewCode("RCN") + NULS = NewCode("NULS") + RDN = NewCode("RDN") + DLT = NewCode("DLT") + AMB = NewCode("AMB") + BCPT = NewCode("BCPT") + ARN = NewCode("ARN") + GVT = NewCode("GVT") + CDT = NewCode("CDT") + POE = NewCode("POE") + QSP = NewCode("QSP") + XZC = NewCode("XZC") + TNT = NewCode("TNT") + FUEL = NewCode("FUEL") + ADX = NewCode("ADX") + CND = NewCode("CND") + LEND = NewCode("LEND") + WABI = NewCode("WABI") + SBTC = NewCode("SBTC") + BCX = NewCode("BCX") + TNB = NewCode("TNB") + GTO = NewCode("GTO") + OST = NewCode("OST") + CVC = NewCode("CVC") + DATA = NewCode("DATA") + ETF = NewCode("ETF") + BRD = NewCode("BRD") + NEBL = NewCode("NEBL") + VIBE = NewCode("VIBE") + LUN = NewCode("LUN") + CHAT = NewCode("CHAT") + RLC = NewCode("RLC") + INS = NewCode("INS") + VIA = NewCode("VIA") + BLZ = NewCode("BLZ") + SYS = NewCode("SYS") + NCASH = NewCode("NCASH") + POA = NewCode("POA") + STORM = NewCode("STORM") + WPR = NewCode("WPR") + QLC = NewCode("QLC") + GRS = NewCode("GRS") + CLOAK = NewCode("CLOAK") + ZEN = NewCode("ZEN") + SKY = NewCode("SKY") + IOTX = NewCode("IOTX") + QKC = NewCode("QKC") + AGI = NewCode("AGI") + NXS = NewCode("NXS") + EON = NewCode("EON") + KEY = NewCode("KEY") + NAS = NewCode("NAS") + ADD = NewCode("ADD") + MEETONE = NewCode("MEETONE") + ATD = NewCode("ATD") + MFT = NewCode("MFT") + EOP = NewCode("EOP") + DENT = NewCode("DENT") + IQ = NewCode("IQ") + DOCK = NewCode("DOCK") + POLY = NewCode("POLY") + VTHO = NewCode("VTHO") + ONG = NewCode("ONG") + PHX = NewCode("PHX") + GO = NewCode("GO") + PAX = NewCode("PAX") + PAXG = NewCode("PAXG") + EDO = NewCode("EDO") + WINGS = NewCode("WINGS") + NAV = NewCode("NAV") + TRIG = NewCode("TRIG") + APPC = NewCode("APPC") + KRW = NewCode("KRW") + HSR = NewCode("HSR") + ETHOS = NewCode("ETHOS") + CTXC = NewCode("CTXC") + ITC = NewCode("ITC") + TRUE = NewCode("TRUE") + ABT = NewCode("ABT") + RNT = NewCode("RNT") + PLY = NewCode("PLY") + PST = NewCode("PST") + KICK = NewCode("KICK") + BTCZ = NewCode("BTCZ") + DXT = NewCode("DXT") + STQ = NewCode("STQ") + INK = NewCode("INK") + HBZ = NewCode("HBZ") + USDT_ETH = NewCode("USDT_ETH") // nolint // Cryptocurrency code + QTUM_ETH = NewCode("QTUM_ETH") // nolint // Cryptocurrency code + BTM_ETH = NewCode("BTM_ETH") // nolint // Cryptocurrency code + FIL = NewCode("FIL") + STX = NewCode("STX") + BOT = NewCode("BOT") + VERI = NewCode("VERI") + ZSC = NewCode("ZSC") + QBT = NewCode("QBT") + MED = NewCode("MED") + QASH = NewCode("QASH") + MDS = NewCode("MDS") + GOD = NewCode("GOD") + SMT = NewCode("SMT") + BTF = NewCode("BTF") + NAS_ETH = NewCode("NAS_ETH") // nolint // Cryptocurrency code + TSL = NewCode("TSL") + BIFI = NewCode("BIFI") + BNTY = NewCode("BNTY") + DRGN = NewCode("DRGN") + GTC = NewCode("GTC") + MDT = NewCode("MDT") + QUN = NewCode("QUN") + GNX = NewCode("GNX") + DDD = NewCode("DDD") + BTO = NewCode("BTO") + TIO = NewCode("TIO") + OCN = NewCode("OCN") + RUFF = NewCode("RUFF") + TNC = NewCode("TNC") + SNET = NewCode("SNET") + COFI = NewCode("COFI") + ZPT = NewCode("ZPT") + JNT = NewCode("JNT") + MTN = NewCode("MTN") + GEM = NewCode("GEM") + DADI = NewCode("DADI") + RFR = NewCode("RFR") + MOBI = NewCode("MOBI") + LEDU = NewCode("LEDU") + DBC = NewCode("DBC") + MKR_OLD = NewCode("MKR_OLD") // nolint // Cryptocurrency code + DPY = NewCode("DPY") + BCDN = NewCode("BCDN") + EOSDAC = NewCode("EOSDAC") + TIPS = NewCode("TIPS") + XMC = NewCode("XMC") + PPS = NewCode("PPS") + BOE = NewCode("BOE") + MEDX = NewCode("MEDX") + SMT_ETH = NewCode("SMT_ETH") // nolint // Cryptocurrency code + CS = NewCode("CS") + MAN = NewCode("MAN") + REM = NewCode("REM") + LYM = NewCode("LYM") + INSTAR = NewCode("INSTAR") + BFT = NewCode("BFT") + IHT = NewCode("IHT") + SENC = NewCode("SENC") + TOMO = NewCode("TOMO") + ELEC = NewCode("ELEC") + SHIP = NewCode("SHIP") + TFD = NewCode("TFD") + HAV = NewCode("HAV") + HUR = NewCode("HUR") + LST = NewCode("LST") + LINO = NewCode("LINO") + SWTH = NewCode("SWTH") + NKN = NewCode("NKN") + SOUL = NewCode("SOUL") + GALA_NEO = NewCode("GALA_NEO") // nolint // Cryptocurrency code + LRN = NewCode("LRN") + GSE = NewCode("GSE") + RATING = NewCode("RATING") + HSC = NewCode("HSC") + HIT = NewCode("HIT") + DX = NewCode("DX") + BXC = NewCode("BXC") + GARD = NewCode("GARD") + FTI = NewCode("FTI") + SOP = NewCode("SOP") + LEMO = NewCode("LEMO") + RED = NewCode("RED") + LBA = NewCode("LBA") + KAN = NewCode("KAN") + OPEN = NewCode("OPEN") + SKM = NewCode("SKM") + NBAI = NewCode("NBAI") + UPP = NewCode("UPP") + ATMI = NewCode("ATMI") + TMT = NewCode("TMT") + BBK = NewCode("BBK") + EDR = NewCode("EDR") + MET = NewCode("MET") + TCT = NewCode("TCT") + EXC = NewCode("EXC") + CNC = NewCode("CNC") + TIX = NewCode("TIX") + XTC = NewCode("XTC") + BU = NewCode("BU") + GNO = NewCode("GNO") + MLN = NewCode("MLN") + XBC = NewCode("XBC") + BTCD = NewCode("BTCD") + BURST = NewCode("BURST") + CLAM = NewCode("CLAM") + XCP = NewCode("XCP") + EMC2 = NewCode("EMC2") + EXP = NewCode("EXP") + FCT = NewCode("FCT") + GAME = NewCode("GAME") + GRC = NewCode("GRC") + HUC = NewCode("HUC") + LBC = NewCode("LBC") + NMC = NewCode("NMC") + NEOS = NewCode("NEOS") + OMNI = NewCode("OMNI") + PASC = NewCode("PASC") + PPC = NewCode("PPC") + DSH = NewCode("DSH") + GML = NewCode("GML") + GSY = NewCode("GSY") + POT = NewCode("POT") + XPM = NewCode("XPM") + AMP = NewCode("AMP") + VRC = NewCode("VRC") + VTC = NewCode("VTC") + ZERO07 = NewCode("007") + BIT16 = NewCode("BIT16") + TWO015 = NewCode("2015") + TWO56 = NewCode("256") + TWOBACCO = NewCode("2BACCO") + TWOGIVE = NewCode("2GIVE") + THIRTY2BIT = NewCode("32BIT") + THREE65 = NewCode("365") + FOUR04 = NewCode("404") + SEVEN00 = NewCode("700") + EIGHTBIT = NewCode("8BIT") + ACLR = NewCode("ACLR") + ACES = NewCode("ACES") + ACPR = NewCode("ACPR") + ACID = NewCode("ACID") + ACOIN = NewCode("ACOIN") + ACRN = NewCode("ACRN") + ADAM = NewCode("ADAM") + ADT = NewCode("ADT") + AIB = NewCode("AIB") + ADZ = NewCode("ADZ") + AECC = NewCode("AECC") + AM = NewCode("AM") + AGRI = NewCode("AGRI") + AGT = NewCode("AGT") + AIR = NewCode("AIR") + ALEX = NewCode("ALEX") + AUM = NewCode("AUM") + ALIEN = NewCode("ALIEN") + ALIS = NewCode("ALIS") + ALL = NewCode("ALL") + ASAFE = NewCode("ASAFE") + AMBER = NewCode("AMBER") + AMS = NewCode("AMS") + ANAL = NewCode("ANAL") + ACP = NewCode("ACP") + ANI = NewCode("ANI") + ANTI = NewCode("ANTI") + ALTC = NewCode("ALTC") + APT = NewCode("APT") + ARCO = NewCode("ARCO") + ALC = NewCode("ALC") + ARB = NewCode("ARB") + ARCT = NewCode("ARCT") + ARCX = NewCode("ARCX") + ARGUS = NewCode("ARGUS") + ARH = NewCode("ARH") + ARM = NewCode("ARM") + ARNA = NewCode("ARNA") + ARPA = NewCode("ARPA") + ARTA = NewCode("ARTA") + ABY = NewCode("ABY") + ARTC = NewCode("ARTC") + AL = NewCode("AL") + ASN = NewCode("ASN") + ADCN = NewCode("ADCN") + ATB = NewCode("ATB") + ATM = NewCode("ATM") + ATMCHA = NewCode("ATMCHA") + ATOM = NewCode("ATOM") + ADC = NewCode("ADC") + ARE = NewCode("ARE") + AUR = NewCode("AUR") + AV = NewCode("AV") + AXIOM = NewCode("AXIOM") + B2B = NewCode("B2B") + B2 = NewCode("B2") + B3 = NewCode("B3") + BAB = NewCode("BAB") + BAN = NewCode("BAN") + BamitCoin = NewCode("BamitCoin") + NANAS = NewCode("NANAS") + BBCC = NewCode("BBCC") + BTA = NewCode("BTA") + BSTK = NewCode("BSTK") + BATL = NewCode("BATL") + BBH = NewCode("BBH") + BITB = NewCode("BITB") + BRDD = NewCode("BRDD") + XBTS = NewCode("XBTS") + BVC = NewCode("BVC") + CHATX = NewCode("CHATX") + BEEP = NewCode("BEEP") + BEEZ = NewCode("BEEZ") + BENJI = NewCode("BENJI") + BERN = NewCode("BERN") + PROFIT = NewCode("PROFIT") + BEST = NewCode("BEST") + BGF = NewCode("BGF") + BIGUP = NewCode("BIGUP") + BLRY = NewCode("BLRY") + BILL = NewCode("BILL") + BIOB = NewCode("BIOB") + BIO = NewCode("BIO") + BIOS = NewCode("BIOS") + BPTN = NewCode("BPTN") + BTCA = NewCode("BTCA") + BA = NewCode("BA") + BAC = NewCode("BAC") + BBT = NewCode("BBT") + BOSS = NewCode("BOSS") + BRONZ = NewCode("BRONZ") + CAT = NewCode("CAT") + BTD = NewCode("BTD") + XBTC21 = NewCode("XBTC21") + BCA = NewCode("BCA") + BCP = NewCode("BCP") + BTDOLL = NewCode("BTDOLL") + LIZA = NewCode("LIZA") + BTCRED = NewCode("BTCRED") + BTCS = NewCode("BTCS") + BTU = NewCode("BTU") + BUM = NewCode("BUM") + LITE = NewCode("LITE") + BCM = NewCode("BCM") + BCS = NewCode("BCS") + BTCU = NewCode("BTCU") + BM = NewCode("BM") + BTCRY = NewCode("BTCRY") + BTCR = NewCode("BTCR") + HIRE = NewCode("HIRE") + STU = NewCode("STU") + BITOK = NewCode("BITOK") + BITON = NewCode("BITON") + BPC = NewCode("BPC") + BPOK = NewCode("BPOK") + BTP = NewCode("BTP") + BITCNY = NewCode("bitCNY") + RNTB = NewCode("RNTB") + BSH = NewCode("BSH") + XBS = NewCode("XBS") + BITS = NewCode("BITS") + BST = NewCode("BST") + BXT = NewCode("BXT") + VEG = NewCode("VEG") + VOLT = NewCode("VOLT") + BTV = NewCode("BTV") + BITZ = NewCode("BITZ") + BTZ = NewCode("BTZ") + BHC = NewCode("BHC") + BDC = NewCode("BDC") + JACK = NewCode("JACK") + BS = NewCode("BS") + BSTAR = NewCode("BSTAR") + BLAZR = NewCode("BLAZR") + BOD = NewCode("BOD") + BLUE = NewCode("BLUE") + BLU = NewCode("BLU") + BLUS = NewCode("BLUS") + BMT = NewCode("BMT") + BOLI = NewCode("BOLI") + BOMB = NewCode("BOMB") + BON = NewCode("BON") + BOOM = NewCode("BOOM") + BOSON = NewCode("BOSON") + BSC = NewCode("BSC") + BRH = NewCode("BRH") + BRAIN = NewCode("BRAIN") + BRE = NewCode("BRE") + BTCM = NewCode("BTCM") + BTCO = NewCode("BTCO") + TALK = NewCode("TALK") + BUB = NewCode("BUB") + BUY = NewCode("BUY") + BUZZ = NewCode("BUZZ") + BTH = NewCode("BTH") + C0C0 = NewCode("C0C0") + CAB = NewCode("CAB") + CF = NewCode("CF") + CLO = NewCode("CLO") + CAM = NewCode("CAM") + CD = NewCode("CD") + CANN = NewCode("CANN") + CNNC = NewCode("CNNC") + CPC = NewCode("CPC") + CST = NewCode("CST") + CAPT = NewCode("CAPT") + CARBON = NewCode("CARBON") + CME = NewCode("CME") + CTK = NewCode("CTK") + CBD = NewCode("CBD") + CCC = NewCode("CCC") + CNT = NewCode("CNT") + XCE = NewCode("XCE") + CHRG = NewCode("CHRG") + CHEMX = NewCode("CHEMX") + CHESS = NewCode("CHESS") + CKS = NewCode("CKS") + CHILL = NewCode("CHILL") + CHIP = NewCode("CHIP") + CHOOF = NewCode("CHOOF") + CRX = NewCode("CRX") + CIN = NewCode("CIN") + POLL = NewCode("POLL") + CLICK = NewCode("CLICK") + CLINT = NewCode("CLINT") + CLUB = NewCode("CLUB") + CLUD = NewCode("CLUD") + COX = NewCode("COX") + COXST = NewCode("COXST") + CFC = NewCode("CFC") + CTIC2 = NewCode("CTIC2") + COIN = NewCode("COIN") + BTTF = NewCode("BTTF") + C2 = NewCode("C2") + CAID = NewCode("CAID") + CL = NewCode("CL") + CTIC = NewCode("CTIC") + CXT = NewCode("CXT") + CHP = NewCode("CHP") + CV2 = NewCode("CV2") + COC = NewCode("COC") + COMP = NewCode("COMP") + CMS = NewCode("CMS") + CONX = NewCode("CONX") + CCX = NewCode("CCX") + CLR = NewCode("CLR") + CORAL = NewCode("CORAL") + CORG = NewCode("CORG") + CSMIC = NewCode("CSMIC") + CMC = NewCode("CMC") + COV = NewCode("COV") + COVX = NewCode("COVX") + CRAB = NewCode("CRAB") + CRAFT = NewCode("CRAFT") + CRNK = NewCode("CRNK") + CRAVE = NewCode("CRAVE") + CRM = NewCode("CRM") + XCRE = NewCode("XCRE") + CREDIT = NewCode("CREDIT") + CREVA = NewCode("CREVA") + CRIME = NewCode("CRIME") + CROC = NewCode("CROC") + CRC = NewCode("CRC") + CRW = NewCode("CRW") + CRY = NewCode("CRY") + CBX = NewCode("CBX") + TKTX = NewCode("TKTX") + CB = NewCode("CB") + CIRC = NewCode("CIRC") + CCB = NewCode("CCB") + CDO = NewCode("CDO") + CG = NewCode("CG") + CJ = NewCode("CJ") + CJC = NewCode("CJC") + CYT = NewCode("CYT") + CRPS = NewCode("CRPS") + PING = NewCode("PING") + CWXT = NewCode("CWXT") + CCT = NewCode("CCT") + CTL = NewCode("CTL") + CURVES = NewCode("CURVES") + CC = NewCode("CC") + CYC = NewCode("CYC") + CYG = NewCode("CYG") + CYP = NewCode("CYP") + FUNK = NewCode("FUNK") + CZECO = NewCode("CZECO") + DALC = NewCode("DALC") + DLISK = NewCode("DLISK") + MOOND = NewCode("MOOND") + DB = NewCode("DB") + DCC = NewCode("DCC") + DCYP = NewCode("DCYP") + DETH = NewCode("DETH") + DKC = NewCode("DKC") + DISK = NewCode("DISK") + DRKT = NewCode("DRKT") + DTT = NewCode("DTT") + DASHS = NewCode("DASHS") + DBTC = NewCode("DBTC") + DCT = NewCode("DCT") + DBET = NewCode("DBET") + DEC = NewCode("DEC") + DECR = NewCode("DECR") + DEA = NewCode("DEA") + DPAY = NewCode("DPAY") + DCRE = NewCode("DCRE") + DC = NewCode("DC") + DES = NewCode("DES") + DEM = NewCode("DEM") + DXC = NewCode("DXC") + DCK = NewCode("DCK") + CUBE = NewCode("CUBE") + DGMS = NewCode("DGMS") + DBG = NewCode("DBG") + DGCS = NewCode("DGCS") + DBLK = NewCode("DBLK") + DIME = NewCode("DIME") + DIRT = NewCode("DIRT") + DVD = NewCode("DVD") + DMT = NewCode("DMT") + NOTE = NewCode("NOTE") + DGORE = NewCode("DGORE") + DLC = NewCode("DLC") + DRT = NewCode("DRT") + DOTA = NewCode("DOTA") + DOX = NewCode("DOX") + DRA = NewCode("DRA") + DFT = NewCode("DFT") + XDB = NewCode("XDB") + DRM = NewCode("DRM") + DRZ = NewCode("DRZ") + DRACO = NewCode("DRACO") + DBIC = NewCode("DBIC") + DUB = NewCode("DUB") + GUM = NewCode("GUM") + DUR = NewCode("DUR") + DUST = NewCode("DUST") + DUX = NewCode("DUX") + DXO = NewCode("DXO") + ECN = NewCode("ECN") + EDR2 = NewCode("EDR2") + EA = NewCode("EA") + EAGS = NewCode("EAGS") + EMT = NewCode("EMT") + EBONUS = NewCode("EBONUS") + ECCHI = NewCode("ECCHI") + EKO = NewCode("EKO") + ECLI = NewCode("ECLI") + ECOB = NewCode("ECOB") + ECO = NewCode("ECO") + EDIT = NewCode("EDIT") + EDRC = NewCode("EDRC") + EDC = NewCode("EDC") + EGAME = NewCode("EGAME") + EGG = NewCode("EGG") + EGO = NewCode("EGO") + ELC = NewCode("ELC") + ELCO = NewCode("ELCO") + ECA = NewCode("ECA") + EPC = NewCode("EPC") + ELE = NewCode("ELE") + ONE337 = NewCode("1337") + EMB = NewCode("EMB") + EMC = NewCode("EMC") + EPY = NewCode("EPY") + EMPC = NewCode("EMPC") + EMP = NewCode("EMP") + ENE = NewCode("ENE") + EET = NewCode("EET") + XNG = NewCode("XNG") + EGMA = NewCode("EGMA") + ENTER = NewCode("ENTER") + ETRUST = NewCode("ETRUST") + EQL = NewCode("EQL") + EQM = NewCode("EQM") + EQT = NewCode("EQT") + ERR = NewCode("ERR") + ESC = NewCode("ESC") + ESP = NewCode("ESP") + ENT = NewCode("ENT") + ETCO = NewCode("ETCO") + DOGETH = NewCode("DOGETH") + ECASH = NewCode("ECASH") + ELITE = NewCode("ELITE") + ETHS = NewCode("ETHS") + ETL = NewCode("ETL") + ETZ = NewCode("ETZ") + EUC = NewCode("EUC") + EURC = NewCode("EURC") + EUROPE = NewCode("EUROPE") + EVA = NewCode("EVA") + EGC = NewCode("EGC") + EOC = NewCode("EOC") + EVIL = NewCode("EVIL") + EVO = NewCode("EVO") + EXB = NewCode("EXB") + EXIT = NewCode("EXIT") + XT = NewCode("XT") + F16 = NewCode("F16") + FADE = NewCode("FADE") + FAZZ = NewCode("FAZZ") + FX = NewCode("FX") + FIDEL = NewCode("FIDEL") + FIDGT = NewCode("FIDGT") + FIND = NewCode("FIND") + FPC = NewCode("FPC") + FIRE = NewCode("FIRE") + FFC = NewCode("FFC") + FRST = NewCode("FRST") + FIST = NewCode("FIST") + FIT = NewCode("FIT") + FLX = NewCode("FLX") + FLVR = NewCode("FLVR") + FLY = NewCode("FLY") + FONZ = NewCode("FONZ") + XFCX = NewCode("XFCX") + FOREX = NewCode("FOREX") + FRN = NewCode("FRN") + FRK = NewCode("FRK") + FRWC = NewCode("FRWC") + FGZ = NewCode("FGZ") + FRE = NewCode("FRE") + FRDC = NewCode("FRDC") + FJC = NewCode("FJC") + FURY = NewCode("FURY") + FSN = NewCode("FSN") + FCASH = NewCode("FCASH") + FTO = NewCode("FTO") + FUZZ = NewCode("FUZZ") + GAKH = NewCode("GAKH") + GBT = NewCode("GBT") + UNITS = NewCode("UNITS") + FOUR20G = NewCode("420G") + GENIUS = NewCode("GENIUS") + GEN = NewCode("GEN") + GEO = NewCode("GEO") + GER = NewCode("GER") + GSR = NewCode("GSR") + SPKTR = NewCode("SPKTR") + GIFT = NewCode("GIFT") + WTT = NewCode("WTT") + GHS = NewCode("GHS") + GIG = NewCode("GIG") + GOT = NewCode("GOT") + XGTC = NewCode("XGTC") + GIZ = NewCode("GIZ") + GLO = NewCode("GLO") + GCR = NewCode("GCR") + BSTY = NewCode("BSTY") + GLC = NewCode("GLC") + GSX = NewCode("GSX") + GOAT = NewCode("GOAT") + GB = NewCode("GB") + GFL = NewCode("GFL") + MNTP = NewCode("MNTP") + GP = NewCode("GP") + GLUCK = NewCode("GLUCK") + GOON = NewCode("GOON") + GTFO = NewCode("GTFO") + GOTX = NewCode("GOTX") + GPU = NewCode("GPU") + GRF = NewCode("GRF") + GRAM = NewCode("GRAM") + GRAV = NewCode("GRAV") + GBIT = NewCode("GBIT") + GREED = NewCode("GREED") + GE = NewCode("GE") + GREENF = NewCode("GREENF") + GRE = NewCode("GRE") + GREXIT = NewCode("GREXIT") + GMCX = NewCode("GMCX") + GROW = NewCode("GROW") + GSM = NewCode("GSM") + GT = NewCode("GT") + NLG = NewCode("NLG") + HKN = NewCode("HKN") + HAC = NewCode("HAC") + HALLO = NewCode("HALLO") + HAMS = NewCode("HAMS") + HPC = NewCode("HPC") + HAWK = NewCode("HAWK") + HAZE = NewCode("HAZE") + HZT = NewCode("HZT") + HDG = NewCode("HDG") + HEDG = NewCode("HEDG") + HEEL = NewCode("HEEL") + HMP = NewCode("HMP") + PLAY = NewCode("PLAY") + HXX = NewCode("HXX") + XHI = NewCode("XHI") + HVCO = NewCode("HVCO") + HTC = NewCode("HTC") + MINH = NewCode("MINH") + HODL = NewCode("HODL") + HON = NewCode("HON") + HOPE = NewCode("HOPE") + HQX = NewCode("HQX") + HSP = NewCode("HSP") + HTML5 = NewCode("HTML5") + HYPERX = NewCode("HYPERX") + HPS = NewCode("HPS") + IOC = NewCode("IOC") + IBANK = NewCode("IBANK") + IBITS = NewCode("IBITS") + ICASH = NewCode("ICASH") + ICOB = NewCode("ICOB") + ICON = NewCode("ICON") + IETH = NewCode("IETH") + ILM = NewCode("ILM") + IMPS = NewCode("IMPS") + NKA = NewCode("NKA") + INCP = NewCode("INCP") + IN = NewCode("IN") + INC = NewCode("INC") + IMS = NewCode("IMS") + IFLT = NewCode("IFLT") + INFX = NewCode("INFX") + INGT = NewCode("INGT") + INPAY = NewCode("INPAY") + INSANE = NewCode("INSANE") + INXT = NewCode("INXT") + IFT = NewCode("IFT") + INV = NewCode("INV") + IVZ = NewCode("IVZ") + ILT = NewCode("ILT") + IONX = NewCode("IONX") + ISL = NewCode("ISL") + ITI = NewCode("ITI") + ING = NewCode("ING") + IEC = NewCode("IEC") + IW = NewCode("IW") + IXC = NewCode("IXC") + IXT = NewCode("IXT") + JPC = NewCode("JPC") + JANE = NewCode("JANE") + JWL = NewCode("JWL") + JIF = NewCode("JIF") + JOBS = NewCode("JOBS") + JOCKER = NewCode("JOCKER") + JW = NewCode("JW") + JOK = NewCode("JOK") + XJO = NewCode("XJO") + KGB = NewCode("KGB") + KARMC = NewCode("KARMC") + KARMA = NewCode("KARMA") + KASHH = NewCode("KASHH") + KAT = NewCode("KAT") + KC = NewCode("KC") + KIDS = NewCode("KIDS") + KIN = NewCode("KIN") + KISS = NewCode("KISS") + KOBO = NewCode("KOBO") + TP1 = NewCode("TP1") + KRAK = NewCode("KRAK") + KGC = NewCode("KGC") + KTK = NewCode("KTK") + KR = NewCode("KR") + KUBO = NewCode("KUBO") + KURT = NewCode("KURT") + KUSH = NewCode("KUSH") + LANA = NewCode("LANA") + LTH = NewCode("LTH") + LAZ = NewCode("LAZ") + LEA = NewCode("LEA") + LEAF = NewCode("LEAF") + LENIN = NewCode("LENIN") + LEPEN = NewCode("LEPEN") + LIR = NewCode("LIR") + LVG = NewCode("LVG") + LGBTQ = NewCode("LGBTQ") + LHC = NewCode("LHC") + EXT = NewCode("EXT") + LBTC = NewCode("LBTC") + LSD = NewCode("LSD") + LIMX = NewCode("LIMX") + LTD = NewCode("LTD") + LINDA = NewCode("LINDA") + LKC = NewCode("LKC") + LBTCX = NewCode("LBTCX") + LCC = NewCode("LCC") + LTCU = NewCode("LTCU") + LTCR = NewCode("LTCR") + LDOGE = NewCode("LDOGE") + LTS = NewCode("LTS") + LIV = NewCode("LIV") + LIZI = NewCode("LIZI") + LOC = NewCode("LOC") + LOCX = NewCode("LOCX") + LOOK = NewCode("LOOK") + LOOT = NewCode("LOOT") + XLTCG = NewCode("XLTCG") + BASH = NewCode("BASH") + LUCKY = NewCode("LUCKY") + L7S = NewCode("L7S") + LDM = NewCode("LDM") + LUMI = NewCode("LUMI") + LUNA = NewCode("LUNA") + LC = NewCode("LC") + LUX = NewCode("LUX") + MCRN = NewCode("MCRN") + XMG = NewCode("XMG") + MMXIV = NewCode("MMXIV") + MAT = NewCode("MAT") + MAO = NewCode("MAO") + MAPC = NewCode("MAPC") + MRB = NewCode("MRB") + MXT = NewCode("MXT") + MARV = NewCode("MARV") + MARX = NewCode("MARX") + MCAR = NewCode("MCAR") + MM = NewCode("MM") + MVC = NewCode("MVC") + MAVRO = NewCode("MAVRO") + MAX = NewCode("MAX") + MAZE = NewCode("MAZE") + MBIT = NewCode("MBIT") + MCOIN = NewCode("MCOIN") + MPRO = NewCode("MPRO") + XMS = NewCode("XMS") + MLITE = NewCode("MLITE") + MLNC = NewCode("MLNC") + MENTAL = NewCode("MENTAL") + MERGEC = NewCode("MERGEC") + MTLMC3 = NewCode("MTLMC3") + METAL = NewCode("METAL") + MUU = NewCode("MUU") + MILO = NewCode("MILO") + MND = NewCode("MND") + XMINE = NewCode("XMINE") + MNM = NewCode("MNM") + XNM = NewCode("XNM") + MIRO = NewCode("MIRO") + MIS = NewCode("MIS") + MMXVI = NewCode("MMXVI") + MOIN = NewCode("MOIN") + MOJO = NewCode("MOJO") + TAB = NewCode("TAB") + MONETA = NewCode("MONETA") + MUE = NewCode("MUE") + MONEY = NewCode("MONEY") + MRP = NewCode("MRP") + MOTO = NewCode("MOTO") + MULTI = NewCode("MULTI") + MST = NewCode("MST") + MVR = NewCode("MVR") + MYSTIC = NewCode("MYSTIC") + WISH = NewCode("WISH") + NKT = NewCode("NKT") + NAT = NewCode("NAT") + ENAU = NewCode("ENAU") + NEBU = NewCode("NEBU") + NEF = NewCode("NEF") + NBIT = NewCode("NBIT") + NETKO = NewCode("NETKO") + NTM = NewCode("NTM") + NETC = NewCode("NETC") + NRC = NewCode("NRC") + NTK = NewCode("NTK") + NTRN = NewCode("NTRN") + NEVA = NewCode("NEVA") + NIC = NewCode("NIC") + NKC = NewCode("NKC") + NYC = NewCode("NYC") + NZC = NewCode("NZC") + NICE = NewCode("NICE") + NDOGE = NewCode("NDOGE") + XTR = NewCode("XTR") + N2O = NewCode("N2O") + NIXON = NewCode("NIXON") + NOC = NewCode("NOC") + NODC = NewCode("NODC") + NODES = NewCode("NODES") + NODX = NewCode("NODX") + NLC = NewCode("NLC") + NLC2 = NewCode("NLC2") + NOO = NewCode("NOO") + NVC = NewCode("NVC") + NPC = NewCode("NPC") + NUBIS = NewCode("NUBIS") + NUKE = NewCode("NUKE") + N7 = NewCode("N7") + NUM = NewCode("NUM") + NMR = NewCode("NMR") + NXE = NewCode("NXE") + OBS = NewCode("OBS") + OCEAN = NewCode("OCEAN") + OCOW = NewCode("OCOW") + EIGHT88 = NewCode("888") + OCC = NewCode("OCC") + OK = NewCode("OK") + ODNT = NewCode("ODNT") + FLAV = NewCode("FLAV") + OLIT = NewCode("OLIT") + OLYMP = NewCode("OLYMP") + OMA = NewCode("OMA") + OMC = NewCode("OMC") + ONEK = NewCode("ONEK") + ONX = NewCode("ONX") + XPO = NewCode("XPO") + OPAL = NewCode("OPAL") + OTN = NewCode("OTN") + OP = NewCode("OP") + OPES = NewCode("OPES") + OPTION = NewCode("OPTION") + ORLY = NewCode("ORLY") + OS76 = NewCode("OS76") + OZC = NewCode("OZC") + P7C = NewCode("P7C") + PAC = NewCode("PAC") + PAK = NewCode("PAK") + PAL = NewCode("PAL") + PND = NewCode("PND") + PINKX = NewCode("PINKX") + POPPY = NewCode("POPPY") + DUO = NewCode("DUO") + PARA = NewCode("PARA") + PKB = NewCode("PKB") + GENE = NewCode("GENE") + PARTY = NewCode("PARTY") + PYN = NewCode("PYN") + XPY = NewCode("XPY") + CON = NewCode("CON") + PAYP = NewCode("PAYP") + GUESS = NewCode("GUESS") + PEN = NewCode("PEN") + PTA = NewCode("PTA") + PEO = NewCode("PEO") + PSB = NewCode("PSB") + XPD = NewCode("XPD") + PXL = NewCode("PXL") + PHR = NewCode("PHR") + PIE = NewCode("PIE") + PIO = NewCode("PIO") + PIPR = NewCode("PIPR") + SKULL = NewCode("SKULL") + PLANET = NewCode("PLANET") + PNC = NewCode("PNC") + XPTX = NewCode("XPTX") + PLNC = NewCode("PLNC") + XPS = NewCode("XPS") + POKE = NewCode("POKE") + PLBT = NewCode("PLBT") + POM = NewCode("POM") + PONZ2 = NewCode("PONZ2") + PONZI = NewCode("PONZI") + XSP = NewCode("XSP") + XPC = NewCode("XPC") + PEX = NewCode("PEX") + TRON = NewCode("TRON") + POST = NewCode("POST") + POSW = NewCode("POSW") + PWR = NewCode("PWR") + POWER = NewCode("POWER") + PRE = NewCode("PRE") + PRS = NewCode("PRS") + PXI = NewCode("PXI") + PEXT = NewCode("PEXT") + PRIMU = NewCode("PRIMU") + PRX = NewCode("PRX") + PRM = NewCode("PRM") + PRIX = NewCode("PRIX") + XPRO = NewCode("XPRO") + PCM = NewCode("PCM") + PROC = NewCode("PROC") + NANOX = NewCode("NANOX") + VRP = NewCode("VRP") + PTY = NewCode("PTY") + PSI = NewCode("PSI") + PSY = NewCode("PSY") + PULSE = NewCode("PULSE") + PUPA = NewCode("PUPA") + PURE = NewCode("PURE") + VIDZ = NewCode("VIDZ") + PUTIN = NewCode("PUTIN") + PX = NewCode("PX") + QTM = NewCode("QTM") + QTZ = NewCode("QTZ") + QBC = NewCode("QBC") + XQN = NewCode("XQN") + RBBT = NewCode("RBBT") + RAC = NewCode("RAC") + RADI = NewCode("RADI") + RAD = NewCode("RAD") + RAI = NewCode("RAI") + XRA = NewCode("XRA") + RATIO = NewCode("RATIO") + REA = NewCode("REA") + RCX = NewCode("RCX") + REE = NewCode("REE") + REC = NewCode("REC") + RMS = NewCode("RMS") + RBIT = NewCode("RBIT") + RNC = NewCode("RNC") + REV = NewCode("REV") + RH = NewCode("RH") + XRL = NewCode("XRL") + RICE = NewCode("RICE") + RICHX = NewCode("RICHX") + RID = NewCode("RID") + RIDE = NewCode("RIDE") + RBT = NewCode("RBT") + RING = NewCode("RING") + RIO = NewCode("RIO") + RISE = NewCode("RISE") + ROCKET = NewCode("ROCKET") + RPC = NewCode("RPC") + ROS = NewCode("ROS") + ROYAL = NewCode("ROYAL") + RSGP = NewCode("RSGP") + RBIES = NewCode("RBIES") + RUBIT = NewCode("RUBIT") + RBY = NewCode("RBY") + RUC = NewCode("RUC") + RUPX = NewCode("RUPX") + RUP = NewCode("RUP") + RUST = NewCode("RUST") + SFE = NewCode("SFE") + SLS = NewCode("SLS") + SMSR = NewCode("SMSR") + RONIN = NewCode("RONIN") + STV = NewCode("STV") + HIFUN = NewCode("HIFUN") + MAD = NewCode("MAD") + SANDG = NewCode("SANDG") + STO = NewCode("STO") + SCAN = NewCode("SCAN") + SCITW = NewCode("SCITW") + SCRPT = NewCode("SCRPT") + SCRT = NewCode("SCRT") + SED = NewCode("SED") + SEEDS = NewCode("SEEDS") + B2X = NewCode("B2X") + SEL = NewCode("SEL") + SLFI = NewCode("SLFI") + SMBR = NewCode("SMBR") + SEN = NewCode("SEN") + SENT = NewCode("SENT") + SRNT = NewCode("SRNT") + SEV = NewCode("SEV") + SP = NewCode("SP") + SXC = NewCode("SXC") + GELD = NewCode("GELD") + SHDW = NewCode("SHDW") + SDC = NewCode("SDC") + SAK = NewCode("SAK") + SHRP = NewCode("SHRP") + SHELL = NewCode("SHELL") + SH = NewCode("SH") + SHORTY = NewCode("SHORTY") + SHREK = NewCode("SHREK") + SHRM = NewCode("SHRM") + SIB = NewCode("SIB") + SIGT = NewCode("SIGT") + SLCO = NewCode("SLCO") + SIGU = NewCode("SIGU") + SIX = NewCode("SIX") + SJW = NewCode("SJW") + SKB = NewCode("SKB") + SW = NewCode("SW") + SLEEP = NewCode("SLEEP") + SLING = NewCode("SLING") + SMART = NewCode("SMART") + SMC = NewCode("SMC") + SMF = NewCode("SMF") + SOCC = NewCode("SOCC") + SCL = NewCode("SCL") + SDAO = NewCode("SDAO") + SOLAR = NewCode("SOLAR") + SOLO = NewCode("SOLO") + SCT = NewCode("SCT") + SONG = NewCode("SONG") + ALTCOM = NewCode("ALTCOM") + SPHTX = NewCode("SPHTX") + SPC = NewCode("SPC") + SPACE = NewCode("SPACE") + SBT = NewCode("SBT") + SPEC = NewCode("SPEC") + SPX = NewCode("SPX") + SCS = NewCode("SCS") + SPORT = NewCode("SPORT") + SPT = NewCode("SPT") + SPR = NewCode("SPR") + SPEX = NewCode("SPEX") + SQL = NewCode("SQL") + SBIT = NewCode("SBIT") + STHR = NewCode("STHR") + STALIN = NewCode("STALIN") + STAR = NewCode("STAR") + STA = NewCode("STA") + START = NewCode("START") + STP = NewCode("STP") + PNK = NewCode("PNK") + STEPS = NewCode("STEPS") + STK = NewCode("STK") + STONK = NewCode("STONK") + STS = NewCode("STS") + STRP = NewCode("STRP") + STY = NewCode("STY") + XMT = NewCode("XMT") + SSTC = NewCode("SSTC") + SUPER = NewCode("SUPER") + SRND = NewCode("SRND") + STRB = NewCode("STRB") + M1 = NewCode("M1") + SPM = NewCode("SPM") + BUCKS = NewCode("BUCKS") + TOKEN = NewCode("TOKEN") + SWT = NewCode("SWT") + SWEET = NewCode("SWEET") + SWING = NewCode("SWING") + CHSB = NewCode("CHSB") + SIC = NewCode("SIC") + SDP = NewCode("SDP") + XSY = NewCode("XSY") + SYNX = NewCode("SYNX") + SNRG = NewCode("SNRG") + TAG = NewCode("TAG") + TAGR = NewCode("TAGR") + TAJ = NewCode("TAJ") + TAK = NewCode("TAK") + TAKE = NewCode("TAKE") + TAM = NewCode("TAM") + XTO = NewCode("XTO") + TAP = NewCode("TAP") + TLE = NewCode("TLE") + TSE = NewCode("TSE") + TLEX = NewCode("TLEX") + TAXI = NewCode("TAXI") + TCN = NewCode("TCN") + TDFB = NewCode("TDFB") + TEAM = NewCode("TEAM") + TECH = NewCode("TECH") + TEC = NewCode("TEC") + TEK = NewCode("TEK") + TB = NewCode("TB") + TLX = NewCode("TLX") + TELL = NewCode("TELL") + TENNET = NewCode("TENNET") + TES = NewCode("TES") + TGS = NewCode("TGS") + XVE = NewCode("XVE") + TCR = NewCode("TCR") + GCC = NewCode("GCC") + MAY = NewCode("MAY") + THOM = NewCode("THOM") + TIA = NewCode("TIA") + TIDE = NewCode("TIDE") + TIE = NewCode("TIE") + TIT = NewCode("TIT") + TTC = NewCode("TTC") + TODAY = NewCode("TODAY") + TBX = NewCode("TBX") + TDS = NewCode("TDS") + TLOSH = NewCode("TLOSH") + TOKC = NewCode("TOKC") + TMRW = NewCode("TMRW") + TOOL = NewCode("TOOL") + TCX = NewCode("TCX") + TOT = NewCode("TOT") + TX = NewCode("TX") + TRANSF = NewCode("TRANSF") + TRAP = NewCode("TRAP") + TBCX = NewCode("TBCX") + TRICK = NewCode("TRICK") + TPG = NewCode("TPG") + TFL = NewCode("TFL") + TRUMP = NewCode("TRUMP") + TNG = NewCode("TNG") + TUR = NewCode("TUR") + TWERK = NewCode("TWERK") + TWIST = NewCode("TWIST") + TWO = NewCode("TWO") + UCASH = NewCode("UCASH") + UAE = NewCode("UAE") + XBU = NewCode("XBU") + UBQ = NewCode("UBQ") + U = NewCode("U") + UDOWN = NewCode("UDOWN") + GAIN = NewCode("GAIN") + USC = NewCode("USC") + UMC = NewCode("UMC") + UNF = NewCode("UNF") + UNIFY = NewCode("UNIFY") + USDE = NewCode("USDE") + UBTC = NewCode("UBTC") + UIS = NewCode("UIS") + UNIT = NewCode("UNIT") + UNI = NewCode("UNI") + UXC = NewCode("UXC") + URC = NewCode("URC") + XUP = NewCode("XUP") + UFR = NewCode("UFR") + URO = NewCode("URO") + UTLE = NewCode("UTLE") + VAL = NewCode("VAL") + VPRC = NewCode("VPRC") + VAPOR = NewCode("VAPOR") + VCOIN = NewCode("VCOIN") + VEC = NewCode("VEC") + VEC2 = NewCode("VEC2") + VLT = NewCode("VLT") + VENE = NewCode("VENE") + VNTX = NewCode("VNTX") + VTN = NewCode("VTN") + CRED = NewCode("CRED") + VERS = NewCode("VERS") + VTX = NewCode("VTX") + VTY = NewCode("VTY") + VIP = NewCode("VIP") + VISIO = NewCode("VISIO") + VK = NewCode("VK") + VOL = NewCode("VOL") + VOYA = NewCode("VOYA") + VPN = NewCode("VPN") + XVS = NewCode("XVS") + VTL = NewCode("VTL") + VULC = NewCode("VULC") + VVI = NewCode("VVI") + WGR = NewCode("WGR") + WAM = NewCode("WAM") + WARP = NewCode("WARP") + WASH = NewCode("WASH") + WGO = NewCode("WGO") + WAY = NewCode("WAY") + WCASH = NewCode("WCASH") + WEALTH = NewCode("WEALTH") + WEEK = NewCode("WEEK") + WHO = NewCode("WHO") + WIC = NewCode("WIC") + WBB = NewCode("WBB") + WINE = NewCode("WINE") + WINK = NewCode("WINK") + WISC = NewCode("WISC") + WITCH = NewCode("WITCH") + WMC = NewCode("WMC") + WOMEN = NewCode("WOMEN") + WOK = NewCode("WOK") + WRT = NewCode("WRT") + XCO = NewCode("XCO") + X2 = NewCode("X2") + XNX = NewCode("XNX") + XAU = NewCode("XAU") + XAV = NewCode("XAV") + XDE2 = NewCode("XDE2") + XDE = NewCode("XDE") + XIOS = NewCode("XIOS") + XOC = NewCode("XOC") + XSSX = NewCode("XSSX") + XBY = NewCode("XBY") + YAC = NewCode("YAC") + YMC = NewCode("YMC") + YAY = NewCode("YAY") + YBC = NewCode("YBC") + YES = NewCode("YES") + YOB2X = NewCode("YOB2X") + YOVI = NewCode("YOVI") + ZYD = NewCode("ZYD") + ZECD = NewCode("ZECD") + ZEIT = NewCode("ZEIT") + ZENI = NewCode("ZENI") + ZET2 = NewCode("ZET2") + ZET = NewCode("ZET") + ZMC = NewCode("ZMC") + ZIRK = NewCode("ZIRK") + ZLQ = NewCode("ZLQ") + ZNE = NewCode("ZNE") + ZONTO = NewCode("ZONTO") + ZOOM = NewCode("ZOOM") + ZRC = NewCode("ZRC") + ZUR = NewCode("ZUR") + ZB = NewCode("ZB") + QC = NewCode("QC") + HLC = NewCode("HLC") + SAFE = NewCode("SAFE") + BTN = NewCode("BTN") + CDC = NewCode("CDC") + DDM = NewCode("DDM") + HOTC = NewCode("HOTC") + BDS = NewCode("BDS") + AAA = NewCode("AAA") + XWC = NewCode("XWC") + PDX = NewCode("PDX") + SLT = NewCode("SLT") + HPY = NewCode("HPY") + XXRP = NewCode("XXRP") // XRP + XXBT = NewCode("XXBT") // BTC, but XXBT instead + XXDG = NewCode("XXDG") // DOGE + XDG = NewCode("XDG") // DOGE + HKD = NewCode("HKD") // Hong Kong Dollar + AUD = NewCode("AUD") // Australian Dollar + USD = NewCode("USD") // United States Dollar + ZUSD = NewCode("ZUSD") // United States Dollar, but with a Z in front of it + EUR = NewCode("EUR") // Euro + ZEUR = NewCode("ZEUR") // Euro, but with a Z in front of it + CAD = NewCode("CAD") // Canadaian Dollar + ZCAD = NewCode("ZCAD") // Canadaian Dollar, but with a Z in front of it + SGD = NewCode("SGD") // Singapore Dollar + RUB = NewCode("RUB") // RUssian ruBle + RUR = NewCode("RUR") // RUssian Ruble + PLN = NewCode("PLN") // Polish zÅ‚oty + TRY = NewCode("TRY") // Turkish lira + UAH = NewCode("UAH") // Ukrainian hryvnia + JPY = NewCode("JPY") // Japanese yen + ZJPY = NewCode("ZJPY") // Japanese yen, but with a Z in front of it + LCH = NewCode("LCH") + MYR = NewCode("MYR") + AFN = NewCode("AFN") + ARS = NewCode("ARS") + AWG = NewCode("AWG") + AZN = NewCode("AZN") + BSD = NewCode("BSD") + BBD = NewCode("BBD") + BYN = NewCode("BYN") + BZD = NewCode("BZD") + BMD = NewCode("BMD") + BOB = NewCode("BOB") + BAM = NewCode("BAM") + BWP = NewCode("BWP") + BGN = NewCode("BGN") + BRL = NewCode("BRL") + BND = NewCode("BND") + KHR = NewCode("KHR") + KYD = NewCode("KYD") + CLP = NewCode("CLP") + CNY = NewCode("CNY") + COP = NewCode("COP") + HRK = NewCode("HRK") + CUP = NewCode("CUP") + CZK = NewCode("CZK") + DKK = NewCode("DKK") + DOP = NewCode("DOP") + XCD = NewCode("XCD") + EGP = NewCode("EGP") + SVC = NewCode("SVC") + FKP = NewCode("FKP") + FJD = NewCode("FJD") + GIP = NewCode("GIP") + GTQ = NewCode("GTQ") + GGP = NewCode("GGP") + GYD = NewCode("GYD") + HNL = NewCode("HNL") + HUF = NewCode("HUF") + ISK = NewCode("ISK") + INR = NewCode("INR") + IDR = NewCode("IDR") + IRR = NewCode("IRR") + IMP = NewCode("IMP") + ILS = NewCode("ILS") + JMD = NewCode("JMD") + JEP = NewCode("JEP") + KZT = NewCode("KZT") + KPW = NewCode("KPW") + KGS = NewCode("KGS") + LAK = NewCode("LAK") + LBP = NewCode("LBP") + LRD = NewCode("LRD") + MKD = NewCode("MKD") + MUR = NewCode("MUR") + MXN = NewCode("MXN") + MNT = NewCode("MNT") + MZN = NewCode("MZN") + NAD = NewCode("NAD") + NPR = NewCode("NPR") + ANG = NewCode("ANG") + NZD = NewCode("NZD") + NIO = NewCode("NIO") + NGN = NewCode("NGN") + NOK = NewCode("NOK") + OMR = NewCode("OMR") + PKR = NewCode("PKR") + PAB = NewCode("PAB") + PYG = NewCode("PYG") + PHP = NewCode("PHP") + QAR = NewCode("QAR") + RON = NewCode("RON") + SHP = NewCode("SHP") + SAR = NewCode("SAR") + RSD = NewCode("RSD") + SCR = NewCode("SCR") + SOS = NewCode("SOS") + ZAR = NewCode("ZAR") + LKR = NewCode("LKR") + SEK = NewCode("SEK") + CHF = NewCode("CHF") + SRD = NewCode("SRD") + SYP = NewCode("SYP") + TWD = NewCode("TWD") + THB = NewCode("THB") + TTD = NewCode("TTD") + TVD = NewCode("TVD") + GBP = NewCode("GBP") + UYU = NewCode("UYU") + UZS = NewCode("UZS") + VEF = NewCode("VEF") + VND = NewCode("VND") + YER = NewCode("YER") + ZWD = NewCode("ZWD") + XETH = NewCode("XETH") + FX_BTC = NewCode("FX_BTC") // nolint // Cryptocurrency code + AAVE = NewCode("AAVE") + YFI = NewCode("YFI") + BAL = NewCode("BAL") + UMA = NewCode("UMA") + SNX = NewCode("SNX") + CRV = NewCode("CRV") + OXT = NewCode("OXT") + BUSD = NewCode("BUSD") + SRM = NewCode("SRM") + FTT = NewCode("FTT") + UGX = NewCode("UGX") // Uganda Shilling + GLM = NewCode("GLM") // Golem + WAXP = NewCode("WAXP") + STRAX = NewCode("STRAX") // Stratis + TMTG = NewCode("TMTG") // The Midas Touch Gold + HDAC = NewCode("HDAC") + AMO = NewCode("AMO") + BSV = NewCode("BSV") + ORBS = NewCode("ORBS") + TFUEL = NewCode("TFUEL") + VALOR = NewCode("VALOR") + ANKR = NewCode("ANKR") + MIX = NewCode("MIX") + CRO = NewCode("CRO") + CHR = NewCode("CHR") + MBL = NewCode("MBL") + MXC = NewCode("MXC") + TRV = NewCode("TRV") + DAD = NewCode("DAD") + WOM = NewCode("WOM") + EM = NewCode("EM") + BOA = NewCode("BOA") + FLETA = NewCode("FLETA") + SXP = NewCode("SXP") + COS = NewCode("COS") + APIX = NewCode("APIX") + EL = NewCode("EL") + BASIC = NewCode("BASIC") + HIV = NewCode("HIV") + XPR = NewCode("XPR") + VRA = NewCode("VRA") + BORA = NewCode("BORA") + APM = NewCode("APM") + CKB = NewCode("CKB") + AERGO = NewCode("AERGO") + ANW = NewCode("ANW") + CENNZ = NewCode("CENNZ") + EVZ = NewCode("EVZ") + CYCLUB = NewCode("CYCLUB") + QTCON = NewCode("QTCON") + RSR = NewCode("RSR") + UOS = NewCode("UOS") + SAND = NewCode("SAND") + STPT = NewCode("STPT") + GOM2 = NewCode("GOM2") + RINGX = NewCode("RINGX") + BEL = NewCode("BEL") + OBSR = NewCode("OBSR") + ORC = NewCode("ORC") + POLA = NewCode("POLA") + AWO = NewCode("AWO") + ADP = NewCode("ADP") + DVI = NewCode("DVI") + IBP = NewCode("IBP") + MIR = NewCode("MIR") + GHX = NewCode("GHX") + BLY = NewCode("BLY") + WOZX = NewCode("WOZX") + ANV = NewCode("ANV") + GRT = NewCode("GRT") + BIOT = NewCode("BIOT") + XNO = NewCode("XNO") + COLA = NewCode("COLA") + NU = NewCode("NU") + LINA = NewCode("LINA") + ASTA = NewCode("ASTA") + MAP = NewCode("MAP") + AQT = NewCode("AQT") + WIKEN = NewCode("WIKEN") + CTSI = NewCode("CTSI") + LPT = NewCode("LPT") + SUSHI = NewCode("SUSHI") + ASM = NewCode("ASM") + CELR = NewCode("CELR") + PUNDIX = NewCode("PUNDIX") + LF = NewCode("LF") + ARW = NewCode("ARW") + MSB = NewCode("MSB") + RLY = NewCode("RLY") + BFC = NewCode("BFC") + ALICE = NewCode("ALICE") + CAKE = NewCode("CAKE") + CHZ = NewCode("CHZ") + AXS = NewCode("AXS") + MATIC = NewCode("MATIC") + BAKE = NewCode("BAKE") + VELO = NewCode("VELO") + GXC = NewCode("GXC") + BTT = NewCode("BTT") + VSYS = NewCode("VSYS") + IPX = NewCode("IPX") + WICC = NewCode("WICC") + META = NewCode("META") + KLAY = NewCode("KLAY") + ALGO = NewCode("ALGO") + JST = NewCode("JST") + MLK = NewCode("MLK") + WEMIX = NewCode("WEMIX") + DOT = NewCode("DOT") + SSX = NewCode("SSX") + TEMCO = NewCode("TEMCO") + HIBS = NewCode("HIBS") + BURGER = NewCode("BURGER") + KSM = NewCode("KSM") + XYM = NewCode("XYM") + SUN = NewCode("SUN") + XEC = NewCode("XEC") + PCI = NewCode("PCI") + SOL = NewCode("SOL") + LN = NewCode("LN") + GUSD = NewCode("GUSD") + AUDIO = NewCode("AUDIO") + EURT = NewCode("EURT") + ALPHA = NewCode("ALPHA") + MCAU = NewCode("MCAU") + AED = NewCode("AED") + BAND = NewCode("BAND") + BCB = NewCode("BCB") + BRZ = NewCode("BRZ") + BTSE = NewCode("BTSE") + FRM = NewCode("FRM") + HXRO = NewCode("HXRO") + LEO = NewCode("LEO") + MBM = NewCode("MBM") + PHNX = NewCode("PHNX") + SFI = NewCode("SFI") + SHIB = NewCode("SHIB") + STAKE = NewCode("STAKE") + SWRV = NewCode("SWRV") + TRYB = NewCode("TRYB") + USDP = NewCode("USDP") + WAUD = NewCode("WAUD") + WCAD = NewCode("WCAD") + WCHF = NewCode("WCHF") + WEUR = NewCode("WEUR") + WGBP = NewCode("WGBP") + WHKD = NewCode("WHKD") + WINR = NewCode("WINR") + WJPY = NewCode("WJPY") + WMYR = NewCode("WMYR") + WOO = NewCode("WOO") + WSGD = NewCode("WSGD") + WUSD = NewCode("WUSD") + WXMR = NewCode("WXMR") + XAUT = NewCode("XAUT") + XSGD = NewCode("XSGD") + EXM = NewCode("EXM") + BTCV = NewCode("BTCV") + CRON = NewCode("CRON") + GNY = NewCode("GNY") + HAI = NewCode("HAI") + HB = NewCode("HB") + HP = NewCode("HP") + IQN = NewCode("IQN") + MNC = NewCode("MNC") + ONE = NewCode("ONE") + PRQ = NewCode("PRQ") + ROOBEE = NewCode("ROOBEE") + TONCOIN = NewCode("TONCOIN") + VLX = NewCode("VLX") + WXT = NewCode("WXT") + UST = NewCode("UST") + USDG = NewCode("USDG") + NYZO = NewCode("NYZO") + ETH2 = NewCode("ETH2") + KAVA = NewCode("KAVA") + RSV = NewCode("RSV") + MTRG = NewCode("MTRG") + COTI = NewCode("COTI") + DIGG = NewCode("DIGG") + YAMV1 = NewCode("YAMV1") + BZRX = NewCode("BZRX") + YAMV2 = NewCode("YAMV2") + BOX = NewCode("BOX") + ERG = NewCode("ERG") + KPHA = NewCode("KPHA") + KAR = NewCode("KAR") + RMRK = NewCode("RMRK") + CRING = NewCode("CRING") + PICA = NewCode("PICA") + XRT = NewCode("XRT") + TEER = NewCode("TEER") + SGB = NewCode("SGB") + KPN = NewCode("KPN") + CSM = NewCode("CSM") + KAZE = NewCode("KAZE") + SASHIMI = NewCode("SASHIMI") + AUCTION = NewCode("AUCTION") + OIN = NewCode("OIN") + ADEL = NewCode("ADEL") + KIMCHI = NewCode("KIMCHI") + CREAM = NewCode("CREAM") + DEGO = NewCode("DEGO") + SFG = NewCode("SFG") + CORE = NewCode("CORE") + ARNX = NewCode("ARNX") + ROSE = NewCode("ROSE") + COVER = NewCode("COVER") + BASE = NewCode("BASE") + HEGIC = NewCode("HEGIC") + DUSK = NewCode("DUSK") + UNFI = NewCode("UNFI") + GHST = NewCode("GHST") + ACH = NewCode("ACH") + FXS = NewCode("FXS") + BORING = NewCode("BORING") + LON = NewCode("LON") + POND = NewCode("POND") + DSD = NewCode("DSD") + SHARE = NewCode("SHARE") + ONC = NewCode("ONC") + ZKS = NewCode("ZKS") + RIF = NewCode("RIF") + PROPS = NewCode("PROPS") + LAYER = NewCode("LAYER") + QNT = NewCode("QNT") + YOP = NewCode("YOP") + BONDED = NewCode("BONDED") + ROOM = NewCode("ROOM") + UNISTAKE = NewCode("UNISTAKE") + FXF = NewCode("FXF") + TORN = NewCode("TORN") + UMB = NewCode("UMB") + JASMY = NewCode("JASMY") + BONDLY = NewCode("BONDLY") + BMI = NewCode("BMI") + RAY = NewCode("RAY") + POLIS = NewCode("POLIS") + WAG = NewCode("WAG") + CYS = NewCode("CYS") + SLRS = NewCode("SLRS") + LIKE = NewCode("LIKE") + PRT = NewCode("PRT") + SUNNY = NewCode("SUNNY") + MNGO = NewCode("MNGO") + STEP = NewCode("STEP") + FIDA = NewCode("FIDA") + PBR = NewCode("PBR") + HOPR = NewCode("HOPR") + PROM = NewCode("PROM") + TVK = NewCode("TVK") + A5T = NewCode("A5T") + CUDOS = NewCode("CUDOS") + COMBO = NewCode("COMBO") + DOWS = NewCode("DOWS") + KYL = NewCode("KYL") + EXRD = NewCode("EXRD") + ETHA = NewCode("ETHA") + ALN = NewCode("ALN") + HAPI = NewCode("HAPI") + BLANK = NewCode("BLANK") + ERN = NewCode("ERN") + KINE = NewCode("KINE") + FET = NewCode("FET") + ZEE = NewCode("ZEE") + POLC = NewCode("POLC") + XED = NewCode("XED") + ANC = NewCode("ANC") + DAFI = NewCode("DAFI") + TARA = NewCode("TARA") + PCNT = NewCode("PCNT") + DG = NewCode("DG") + SPI = NewCode("SPI") + BANK = NewCode("BANK") + UMX = NewCode("UMX") + TIDAL = NewCode("TIDAL") + LABS = NewCode("LABS") + OGN = NewCode("OGN") + BLES = NewCode("BLES") + OVR = NewCode("OVR") + HGET = NewCode("HGET") + NOIA = NewCode("NOIA") + COOK = NewCode("COOK") + FST = NewCode("FST") + AME = NewCode("AME") + STN = NewCode("STN") + SHOPX = NewCode("SHOPX") + SHFT = NewCode("SHFT") + RBC = NewCode("RBC") + VAI = NewCode("VAI") + FEI = NewCode("FEI") + XEND = NewCode("XEND") + SUKU = NewCode("SUKU") + LTO = NewCode("LTO") + TOTM = NewCode("TOTM") + RAZE = NewCode("RAZE") + DUCK2 = NewCode("DUCK2") + CEL = NewCode("CEL") + DDIM = NewCode("DDIM") + TLM = NewCode("TLM") + DDOS = NewCode("DDOS") + GS = NewCode("GS") + RAGE = NewCode("RAGE") + AKITA = NewCode("AKITA") + FORTH = NewCode("FORTH") + CARDS = NewCode("CARDS") + HORD = NewCode("HORD") + WBTC = NewCode("WBTC") + ARES = NewCode("ARES") + SUSD = NewCode("SUSD") + TCP = NewCode("TCP") + BLACK = NewCode("BLACK") + EZ = NewCode("EZ") + VSO = NewCode("VSO") + XAVA = NewCode("XAVA") + PNG = NewCode("PNG") + LOCG = NewCode("LOCG") + WSIENNA = NewCode("WSIENNA") + STBU = NewCode("STBU") + DFND = NewCode("DFND") + GDT = NewCode("GDT") + PRARE = NewCode("PRARE") + GYEN = NewCode("GYEN") + METIS = NewCode("METIS") + BZZ = NewCode("BZZ") + TENSET = NewCode("10SET") + STRING = NewCode("STRING") + PDEX = NewCode("PDEX") + FEAR = NewCode("FEAR") + ELON = NewCode("ELON") + NOA = NewCode("NOA") + NAOS = NewCode("NAOS") + GITCOIN = NewCode("GITCOIN") + XCAD = NewCode("XCAD") + LSS = NewCode("LSS") + CVX = NewCode("CVX") + PHTR = NewCode("PHTR") + APN = NewCode("APN") + DFYN = NewCode("DFYN") + LIME = NewCode("LIME") + FORM = NewCode("FORM") + KEX = NewCode("KEX") + DLTA = NewCode("DLTA") + DPR = NewCode("DPR") + CQT = NewCode("CQT") + OLY = NewCode("OLY") + FUSE = NewCode("FUSE") + SRK = NewCode("SRK") + BURP = NewCode("BURP") + CART = NewCode("CART") + C98 = NewCode("C98") + DNXC = NewCode("DNXC") + DERC = NewCode("DERC") + PLA = NewCode("PLA") + EFI = NewCode("EFI") + HMT = NewCode("HMT") + SKT = NewCode("SKT") + SPHRI = NewCode("SPHRI") + BIT = NewCode("BIT") + RARE = NewCode("RARE") + ZLW = NewCode("ZLW") + SKYRIM = NewCode("SKYRIM") + OCT = NewCode("OCT") + ATA = NewCode("ATA") + PUSH = NewCode("PUSH") + REVO = NewCode("REVO") + VENT = NewCode("VENT") + LDO = NewCode("LDO") + GEL = NewCode("GEL") + CTRC = NewCode("CTRC") + ITGR = NewCode("ITGR") + HOTCROSS = NewCode("HOTCROSS") + OPUL = NewCode("OPUL") + POLI = NewCode("POLI") + TAUR = NewCode("TAUR") + EQX = NewCode("EQX") + RBN = NewCode("RBN") + PHM = NewCode("PHM") + FLOKI = NewCode("FLOKI") + CIRUS = NewCode("CIRUS") + DYDX = NewCode("DYDX") + RGT = NewCode("RGT") + AGLD = NewCode("AGLD") + DOGNFT = NewCode("DOGNFT") + SOV = NewCode("SOV") + URUS = NewCode("URUS") + CFG = NewCode("CFG") + TBTC = NewCode("TBTC") + NFTX = NewCode("NFTX") + ORAI = NewCode("ORAI") + LIT = NewCode("LIT") + POOLZ = NewCode("POOLZ") + DODO = NewCode("DODO") + IPAD = NewCode("IPAD") + OPIUM = NewCode("OPIUM") + REEF = NewCode("REEF") + MAPS = NewCode("MAPS") + ZCN = NewCode("ZCN") + BAO = NewCode("BAO") + DIS = NewCode("DIS") + PBTC35A = NewCode("PBTC35A") + NORD = NewCode("NORD") + FLOW = NewCode("FLOW") + FIN = NewCode("FIN") + INJ = NewCode("INJ") + KP3R = NewCode("KP3R") + HYVE = NewCode("HYVE") + RAMP = NewCode("RAMP") + RARI = NewCode("RARI") + MPH = NewCode("MPH") + CVP = NewCode("CVP") + VALUE = NewCode("VALUE") + YFII = NewCode("YFII") + TROY = NewCode("TROY") + SPA = NewCode("SPA") + FOR = NewCode("FOR") + DIA = NewCode("DIA") + TRB = NewCode("TRB") + PEARL = NewCode("PEARL") + NFT = NewCode("NFT") + SLM = NewCode("SLM") + TAI = NewCode("TAI") + JFI = NewCode("JFI") + DKA = NewCode("DKA") + DOS = NewCode("DOS") + LBK = NewCode("LBK") + ASD = NewCode("ASD") + SWOP = NewCode("SWOP") + WEST = NewCode("WEST") + HYDRA = NewCode("HYDRA") + OLT = NewCode("OLT") + LAT = NewCode("LAT") + STC = NewCode("STC") + HNT = NewCode("HNT") + AKT = NewCode("AKT") + BTC3L = NewCode("BTC3L") + COTI3L = NewCode("COTI3L") + XCH3L = NewCode("XCH3L") + IOST3L = NewCode("IOST3L") + BZZ3L = NewCode("BZZ3L") + TRIBE3L = NewCode("TRIBE3L") + RAY3L = NewCode("RAY3L") + AR3L = NewCode("AR3L") + ONE3L = NewCode("ONE3L") + HBAR3L = NewCode("HBAR3L") + CSPR3L = NewCode("CSPR3L") + SXP3L = NewCode("SXP3L") + XEC3L = NewCode("XEC3L") + LIT3L = NewCode("LIT3L") + MINA3L = NewCode("MINA3L") + GALA3L = NewCode("GALA3L") + FTT3L = NewCode("FTT3L") + C983L = NewCode("C983L") + DYDX3L = NewCode("DYDX3L") + MTL3L = NewCode("MTL3L") + FTM3L = NewCode("FTM3L") + SAND3L = NewCode("SAND3L") + LUNA3L = NewCode("LUNA3L") + ALPHA3L = NewCode("ALPHA3L") + RUNE3L = NewCode("RUNE3L") + ICP3L = NewCode("ICP3L") + SHIB3L = NewCode("SHIB3L") + ACH3L = NewCode("ACH3L") + ALICE3L = NewCode("ALICE3L") + AXS3L = NewCode("AXS3L") + MATIC3L = NewCode("MATIC3L") + BTC5L = NewCode("BTC5L") + BCH5L = NewCode("BCH5L") + DOT5L = NewCode("DOT5L") + XRP5L = NewCode("XRP5L") + BSV5L = NewCode("BSV5L") + LTC5L = NewCode("LTC5L") + EOS5L = NewCode("EOS5L") + ETH5L = NewCode("ETH5L") + LINK3L = NewCode("LINK3L") + KAVA3L = NewCode("KAVA3L") + EGLD3L = NewCode("EGLD3L") + CHZ3L = NewCode("CHZ3L") + MKR3L = NewCode("MKR3L") + LRC3L = NewCode("LRC3L") + BAL3L = NewCode("BAL3L") + JST3L = NewCode("JST3L") + SERO3L = NewCode("SERO3L") + VET3L = NewCode("VET3L") + THETA3L = NewCode("THETA3L") + ZIL3L = NewCode("ZIL3L") + GRIN3L = NewCode("GRIN3L") + BEAM3L = NewCode("BEAM3L") + SOL3L = NewCode("SOL3L") + SKL3L = NewCode("SKL3L") + ONEINCH3L = NewCode("1INCH3L") + LON3L = NewCode("LON3L") + DOGE3L = NewCode("DOGE3L") + GRT3L = NewCode("GRT3L") + BNB3L = NewCode("BNB3L") + TRX3L = NewCode("TRX3L") + ATOM3L = NewCode("ATOM3L") + AVAX3L = NewCode("AVAX3L") + NEAR3L = NewCode("NEAR3L") + ROSE3L = NewCode("ROSE3L") + ZEN3L = NewCode("ZEN3L") + QTUM3L = NewCode("QTUM3L") + XLM3L = NewCode("XLM3L") + XRP3L = NewCode("XRP3L") + CFX3L = NewCode("CFX3L") + OMG3L = NewCode("OMG3L") + ALGO3L = NewCode("ALGO3L") + WAVES3L = NewCode("WAVES3L") + NEO3L = NewCode("NEO3L") + ONT3L = NewCode("ONT3L") + ETC3L = NewCode("ETC3L") + CVC3L = NewCode("CVC3L") + SNX3L = NewCode("SNX3L") + ADA3L = NewCode("ADA3L") + DASH3L = NewCode("DASH3L") + AAVE3L = NewCode("AAVE3L") + SRM3L = NewCode("SRM3L") + KSM3L = NewCode("KSM3L") + BTM3L = NewCode("BTM3L") + ZEC3L = NewCode("ZEC3L") + XMR3L = NewCode("XMR3L") + AMPL3L = NewCode("AMPL3L") + CRV3L = NewCode("CRV3L") + COMP3L = NewCode("COMP3L") + YFII3L = NewCode("YFII3L") + YFI3L = NewCode("YFI3L") + HT3L = NewCode("HT3L") + OKB3L = NewCode("OKB3L") + UNI3L = NewCode("UNI3L") + DOT3L = NewCode("DOT3L") + FIL3L = NewCode("FIL3L") + SUSHI3L = NewCode("SUSHI3L") + ETH3L = NewCode("ETH3L") + EOS3L = NewCode("EOS3L") + BSV3L = NewCode("BSV3L") + BCH3L = NewCode("BCH3L") + LTC3L = NewCode("LTC3L") + XTZ3L = NewCode("XTZ3L") + RVN = NewCode("RVN") + AR = NewCode("AR") + SNK = NewCode("SNK") + NSDX = NewCode("NSDX") + HIVE = NewCode("HIVE") + BCHA = NewCode("BCHA") + FLUX = NewCode("FLUX") + NAX = NewCode("NAX") + NBOT = NewCode("NBOT") + BEAM = NewCode("BEAM") + MINA = NewCode("MINA") + ABBC = NewCode("ABBC") + FIC = NewCode("FIC") + STOX = NewCode("STOX") + VIDYX = NewCode("VIDYX") + CNNS = NewCode("CNNS") + BTCBEAR = NewCode("BTCBEAR") + ETHBULL = NewCode("ETHBULL") + EOSBEAR = NewCode("EOSBEAR") + XRPBULL = NewCode("XRPBULL") + WGRT = NewCode("WGRT") + RUNE = NewCode("RUNE") + CBK = NewCode("CBK") + OPA = NewCode("OPA") + KABY = NewCode("KABY") + BP = NewCode("BP") + SFUND = NewCode("SFUND") + ASTRO = NewCode("ASTRO") + ARV = NewCode("ARV") + ROSN = NewCode("ROSN") + CPHR = NewCode("CPHR") + KWS = NewCode("KWS") + CTT = NewCode("CTT") + BEEFI = NewCode("BEEFI") + BLIN = NewCode("BLIN") + XPNET = NewCode("XPNET") + BABY = NewCode("BABY") + OPS = NewCode("OPS") + RACA = NewCode("RACA") + HOD = NewCode("HOD") + OLYMPUS = NewCode("OLYMPUS") + BMON = NewCode("BMON") + PVU = NewCode("PVU") + FAN = NewCode("FAN") + SKILL = NewCode("SKILL") + SPS = NewCode("SPS") + HERO = NewCode("HERO") + FEVR = NewCode("FEVR") + WEX = NewCode("WEX") + KALM = NewCode("KALM") + KPAD = NewCode("KPAD") + BABYDOGE = NewCode("BABYDOGE") + PIG = NewCode("PIG") + FINE = NewCode("FINE") + BSCS = NewCode("BSCS") + SAFEMARS = NewCode("SAFEMARS") + PSG = NewCode("PSG") + PET = NewCode("PET") + ALPACA = NewCode("ALPACA") + BRY = NewCode("BRY") + TOOLS = NewCode("TOOLS") + JULD = NewCode("JULD") + FRA = NewCode("FRA") + TWT = NewCode("TWT") + WIN = NewCode("WIN") + MTV = NewCode("MTV") + HPB = NewCode("HPB") + EGLD = NewCode("EGLD") + CSPR = NewCode("CSPR") + FIS = NewCode("FIS") + MDX = NewCode("MDX") + WAR = NewCode("WAR") + XNFT = NewCode("XNFT") + BXH = NewCode("BXH") + BAGS = NewCode("BAGS") + ALEPH = NewCode("ALEPH") + KEEP = NewCode("KEEP") + NXM = NewCode("NXM") + ONEINCH = NewCode("ONEINCH") + SKL = NewCode("SKL") + BOND = NewCode("BOND") + ALCX = NewCode("ALCX") + API3 = NewCode("API3") + DDX = NewCode("DDX") + FTM = NewCode("FTM") + CTX = NewCode("CTX") + ILV = NewCode("ILV") + MC02 = NewCode("MC02") + SLP = NewCode("SLP") + WTON = NewCode("WTON") + EFIL = NewCode("EFIL") + MTX = NewCode("MTX") + YGG = NewCode("YGG") + QCASH = NewCode("QCASH") + TV = NewCode("TV") + BCW = NewCode("BCW") + ENTC = NewCode("ENTC") + XWCC = NewCode("XWCC") + BRC = NewCode("BRC") + GRIN = NewCode("GRIN") + B91 = NewCode("B91") + YTNB = NewCode("YTNB") + NWT = NewCode("NWT") + BAR = NewCode("BAR") + ACC = NewCode("ACC") + HX = NewCode("HX") + LVN = NewCode("LVN") + TSR = NewCode("TSR") + FN = NewCode("FN") + HNS = NewCode("HNS") + KPG = NewCode("KPG") + LTG = NewCode("LTG") + UFO = NewCode("UFO") + GUCS = NewCode("GUCS") + VBT = NewCode("VBT") + DSF = NewCode("DSF") + GST = NewCode("GST") + DAWN = NewCode("DAWN") + UFC = NewCode("UFC") + EP = NewCode("EP") + ULU = NewCode("ULU") + DMD = NewCode("DMD") + NBS = NewCode("NBS") + BGPT = NewCode("BGPT") + DIP = NewCode("DIP") + QFIL = NewCode("QFIL") + RTF = NewCode("RTF") + M = NewCode("M") + FOMP = NewCode("FOMP") + BDM = NewCode("BDM") + DORA = NewCode("DORA") + UZ = NewCode("UZ") + BKH = NewCode("BKH") + CRU = NewCode("CRU") + IDV = NewCode("IDV") + NEAR = NewCode("NEAR") + DFL = NewCode("DFL") + BED = NewCode("BED") + SDOG = NewCode("SDOG") + CFX = NewCode("CFX") + CATE = NewCode("CATE") + ONETHOUSANDHOKK = NewCode("1000HOKK") + ONETHOUSANDKISHU = NewCode("1000KISHU") + XFLR = NewCode("XFLR") + ICP = NewCode("ICP") + BNA = NewCode("BNA") + DOM = NewCode("DOM") + POLS = NewCode("POLS") + O3 = NewCode("O3") + CLV = NewCode("CLV") + FARM = NewCode("FARM") + ORN = NewCode("ORN") + QUICK = NewCode("QUICK") + TRU = NewCode("TRU") + SANA = NewCode("SANA") + TRIBE = NewCode("TRIBE") + CELO = NewCode("CELO") + SDN = NewCode("SDN") + WNCG = NewCode("WNCG") + AMC = NewCode("AMC") + OOE = NewCode("OOE") + XYO = NewCode("XYO") + GALA = NewCode("GALA") + ZKN = NewCode("ZKN") + XCH = NewCode("XCH") + AC = NewCode("AC") + ABTC = NewCode("ABTC") + AFC = NewCode("AFC") + AGE = NewCode("AGE") + AIN = NewCode("AIN") + ALI = NewCode("ALI") + ALIX = NewCode("ALIX") + ANJ = NewCode("ANJ") + ANRX = NewCode("ANRX") + ANY = NewCode("ANY") + AOS = NewCode("AOS") + AQUAGOAT = NewCode("AQUAGOAT") + ARTCN = NewCode("ARTCN") + ARTE = NewCode("ARTE") + AT = NewCode("AT") + ATC = NewCode("ATC") + ATLAS = NewCode("ATLAS") + ATP = NewCode("ATP") + ATPNAS = NewCode("ATPNAS") + AURY = NewCode("AURY") + AUSD = NewCode("AUSD") + AUTO = NewCode("AUTO") + AVAX = NewCode("AVAX") + AVF = NewCode("AVF") + AWR = NewCode("AWR") + B20 = NewCode("B20") + BADGER = NewCode("BADGER") + BAFE = NewCode("BAFE") + BANANA = NewCode("BANANA") + BAS = NewCode("BAS") + BASEL = NewCode("BASEL") + BASID = NewCode("BASID") + BBC = NewCode("BBC") + BBCNP = NewCode("BBCNP") + BCK = NewCode("BCK") + BDP = NewCode("BDP") + BELT = NewCode("BELT") + SLIM = NewCode("SLIM") + SPN = NewCode("SPN") + VUSD = NewCode("VUSD") + POLYBUNNY = NewCode("POLYBUNNY") + STARL = NewCode("STARL") + KISC = NewCode("KISC") + MASS = NewCode("MASS") + MOYU = NewCode("MOYU") + PLUG = NewCode("PLUG") + SFC = NewCode("SFC") + TEP = NewCode("TEP") + GOFX = NewCode("GOFX") + KAINET = NewCode("KAINET") + BXA = NewCode("BXA") + SLOT = NewCode("SLOT") + EXVA = NewCode("EXVA") + MW = NewCode("MW") + BOO = NewCode("BOO") + BZKY = NewCode("BZKY") + NFTART = NewCode("NFTART") + QRDO = NewCode("QRDO") + SHILL = NewCode("SHILL") + SIT = NewCode("SIT") + USF = NewCode("USF") + EBSO = NewCode("EBSO") + GUSDT = NewCode("GUSDT") + BTRST = NewCode("BTRST") + DBX = NewCode("DBX") + MARSINU = NewCode("MARSINU") + GEMG = NewCode("GEMG") + HYPE = NewCode("HYPE") + ELCASH = NewCode("ELCASH") + FEG = NewCode("FEG") + MTC = NewCode("MTC") + NCT = NewCode("NCT") + PSYDUCK = NewCode("PSYDUCK") + SEAL = NewCode("SEAL") + DOGEKONGZILLA = NewCode("DOGEKONGZILLA") + DOUGH = NewCode("DOUGH") + SURFMOON = NewCode("SURFMOON") + BUIDL = NewCode("BUIDL") + DOGGY = NewCode("DOGGY") + VNX = NewCode("VNX") + BSB = NewCode("BSB") + GOF = NewCode("GOF") + GM = NewCode("GM") + TEN = NewCode("TEN") + CRT = NewCode("CRT") + FIL12 = NewCode("FIL12") + WAXE = NewCode("WAXE") + VEGA = NewCode("VEGA") + LSP = NewCode("LSP") + TOWER = NewCode("TOWER") + GL = NewCode("GL") + IBNB = NewCode("IBNB") + WDS = NewCode("WDS") + YYE = NewCode("YYE") + GHC = NewCode("GHC") + LBKL = NewCode("LBKL") + NASADOGE = NewCode("NASADOGE") + TKX = NewCode("TKX") + XWC2 = NewCode("XWC2") + CNEX = NewCode("CNEX") + DOKI = NewCode("DOKI") + MX = NewCode("MX") + UCA = NewCode("UCA") + NKGEN = NewCode("NKGEN") + STPL = NewCode("STPL") + CYE = NewCode("CYE") + KBC = NewCode("KBC") + X = NewCode("X") + GINU = NewCode("GINU") + MEDA = NewCode("MEDA") + TREES = NewCode("TREES") + UZUMAKI = NewCode("UZUMAKI") + CHLT = NewCode("CHLT") + DBZ = NewCode("DBZ") + IMX = NewCode("IMX") + LIEN = NewCode("LIEN") + ULTRA = NewCode("ULTRA") + BTSC = NewCode("BTSC") + EAI = NewCode("EAI") + CORGI = NewCode("CORGI") + MINISHIBA = NewCode("MINISHIBA") + BLOC = NewCode("BLOC") + PEPPA = NewCode("PEPPA") + FOUR = NewCode("FOUR") + PERP = NewCode("PERP") + THG = NewCode("THG") + COLLIE = NewCode("COLLIE") + FO = NewCode("FO") + HER = NewCode("HER") + DNS = NewCode("DNS") + ELS = NewCode("ELS") + MINISAITAMA = NewCode("MINISAITAMA") + PCH = NewCode("PCH") + SBREE = NewCode("SBREE") + BPRIVA = NewCode("BPRIVA") + DLX = NewCode("DLX") + NAFT = NewCode("NAFT") + SHIBLITE = NewCode("SHIBLITE") + BHD = NewCode("BHD") + THN = NewCode("THN") // nolint:misspell // false positive + DOGEDASH = NewCode("DOGEDASH") + FARA = NewCode("FARA") + FIL120 = NewCode("FIL120") + RABBIT = NewCode("RABBIT") + ZOON = NewCode("ZOON") + BONFIRE = NewCode("BONFIRE") + CHAIN = NewCode("CHAIN") + GGC = NewCode("GGC") + IOG = NewCode("IOG") + MEME = NewCode("MEME") + PINU = NewCode("PINU") + CCASH = NewCode("CCASH") + GART = NewCode("GART") + VALK = NewCode("VALK") + LM = NewCode("LM") + MINIDOGE = NewCode("MINIDOGE") + RAZOR = NewCode("RAZOR") + KILL = NewCode("KILL") + MASK = NewCode("MASK") + BUMN = NewCode("BUMN") + KLAYG = NewCode("KLAYG") + MICROSHIB = NewCode("MICROSHIB") + IDHUB = NewCode("IDHUB") + JT = NewCode("JT") + NTX = NewCode("NTX") + SAMO = NewCode("SAMO") + SANSHU = NewCode("SANSHU") + TASTE = NewCode("TASTE") + CXC = NewCode("CXC") + FLDT = NewCode("FLDT") + SAITO = NewCode("SAITO") + UIP = NewCode("UIP") + HTDF = NewCode("HTDF") + MOONRISE = NewCode("MOONRISE") + HOKK = NewCode("HOKK") + LT = NewCode("LT") + MINI = NewCode("MINI") + MOK = NewCode("MOK") + BLUESPARROW = NewCode("BLUESPARROW") + FTS = NewCode("FTS") + PN = NewCode("PN") + KDC = NewCode("KDC") + SAFEMOON = NewCode("SAFEMOON") + SON = NewCode("SON") + ZINU = NewCode("ZINU") + FIL72 = NewCode("FIL72") + PETS = NewCode("PETS") + POK = NewCode("POK") + RBASE = NewCode("RBASE") + TOKAU = NewCode("TOKAU") + UBEX = NewCode("UBEX") + VENA = NewCode("VENA") + FC = NewCode("FC") + OEX = NewCode("OEX") + DOGEBACK = NewCode("DOGEBACK") + IOEX = NewCode("IOEX") + MOVR = NewCode("MOVR") + PTT = NewCode("PTT") + ZOOT = NewCode("ZOOT") + CATGIRL = NewCode("CATGIRL") + CHOPPER = NewCode("CHOPPER") + EDEN = NewCode("EDEN") + GEP = NewCode("GEP") + LVI = NewCode("LVI") + PEG = NewCode("PEG") + SUTER = NewCode("SUTER") + CHECK = NewCode("CHECK") + DRO = NewCode("DRO") + FBC = NewCode("FBC") + KABOSU = NewCode("KABOSU") + CPX = NewCode("CPX") + ZAK = NewCode("ZAK") + ETERNAL = NewCode("ETERNAL") + MILKTEA = NewCode("MILKTEA") + KEANU = NewCode("KEANU") + NSFW = NewCode("NSFW") + XOM = NewCode("XOM") + EMPIRE = NewCode("EMPIRE") + FNK = NewCode("FNK") + SMRAT = NewCode("SMRAT") + TAPE = NewCode("TAPE") + IIC = NewCode("IIC") + IMI = NewCode("IMI") + KIWI = NewCode("KIWI") + POLO = NewCode("POLO") + BRIGHT = NewCode("BRIGHT") + HIKO = NewCode("HIKO") + HELIOS = NewCode("HELIOS") + KINGSHIB = NewCode("KINGSHIB") + DOGE2 = NewCode("DOGE2") + EFK = NewCode("EFK") + LMCSWAP = NewCode("LMCSWAP") + PMON = NewCode("PMON") + POODL = NewCode("POODL") + SSN = NewCode("SSN") + BIN = NewCode("BIN") + LFIL = NewCode("LFIL") + BFDT = NewCode("BFDT") + USDN = NewCode("USDN") + GDOGE = NewCode("GDOGE") + HUSD = NewCode("HUSD") + FOIN = NewCode("FOIN") + LARIX = NewCode("LARIX") + MARSRISE = NewCode("MARSRISE") + PUT = NewCode("PUT") + ZIQ = NewCode("ZIQ") + CCAR = NewCode("CCAR") + CZ = NewCode("CZ") + PLUGCN = NewCode("PLUGCN") + X2P = NewCode("X2P") + MOONX = NewCode("MOONX") + TUDA = NewCode("TUDA") + ZOE = NewCode("ZOE") + FCF = NewCode("FCF") + GHD = NewCode("GHD") + KALA = NewCode("KALA") + ULTI = NewCode("ULTI") + GRAMS = NewCode("GRAMS") + ODA = NewCode("ODA") + PHV = NewCode("PHV") + SAL = NewCode("SAL") + TKY = NewCode("TKY") + XWG = NewCode("XWG") + CCTC = NewCode("CCTC") + MKCY = NewCode("MKCY") + LFIL36 = NewCode("LFIL36") + PKMON = NewCode("PKMON") + RCKT = NewCode("RCKT") + VCC = NewCode("VCC") + CUMSTAR = NewCode("CUMSTAR") + JNTR = NewCode("JNTR") + JIND = NewCode("JIND") + SAITAMA = NewCode("SAITAMA") + ELT = NewCode("ELT") + FLOKIN = NewCode("FLOKIN") + NEX = NewCode("NEX") + TENA = NewCode("TENA") + CAP = NewCode("CAP") + LUFFY = NewCode("LUFFY") + ET = NewCode("ET") + DBNK = NewCode("DBNK") + SDT = NewCode("SDT") + NWC = NewCode("NWC") + PAMP = NewCode("PAMP") + XVIX = NewCode("XVIX") + BLADE = NewCode("BLADE") + GETH = NewCode("GETH") + HIGH = NewCode("HIGH") + PLF = NewCode("PLF") + DSG = NewCode("DSG") + GN = NewCode("GN") + TTT = NewCode("TTT") + HMR = NewCode("HMR") + SMD = NewCode("SMD") + WEYU = NewCode("WEYU") + BIKI = NewCode("BIKI") + VIKINGS = NewCode("VIKINGS") + BUGG = NewCode("BUGG") + LUNAPAD = NewCode("LUNAPAD") + EAURIC = NewCode("EAURIC") + HJW = NewCode("HJW") + LUC = NewCode("LUC") + BV = NewCode("BV") + COGE = NewCode("COGE") + DILI = NewCode("DILI") + XHDX = NewCode("XHDX") + XP = NewCode("XP") + XSTAR = NewCode("XSTAR") + FKX = NewCode("FKX") + RPL = NewCode("RPL") + JUS = NewCode("JUS") + KISHIMOTO = NewCode("KISHIMOTO") + NEST = NewCode("NEST") + SMBSWAP = NewCode("SMBSWAP") + WOLVERINU = NewCode("WOLVERINU") + GNBT = NewCode("GNBT") + HDS = NewCode("HDS") + QNUT = NewCode("QNUT") + ENS = NewCode("ENS") + FOG = NewCode("FOG") + NBTC = NewCode("NBTC") + CHS = NewCode("CHS") + GMT = NewCode("GMT") + ORCA = NewCode("ORCA") + SERO = NewCode("SERO") + BGLD = NewCode("BGLD") + CDB = NewCode("CDB") + SLA = NewCode("SLA") + UT = NewCode("UT") + POLYDOGE = NewCode("POLYDOGE") + SFP = NewCode("SFP") + HKUN = NewCode("HKUN") + WHALE = NewCode("WHALE") + CCXX = NewCode("CCXX") + DOR = NewCode("DOR") + OATH = NewCode("OATH") + GKI = NewCode("GKI") + PANDA = NewCode("PANDA") + OVO = NewCode("OVO") + CELT = NewCode("CELT") + OSST = NewCode("OSST") + OMNIS = NewCode("OMNIS") + TONE = NewCode("TONE") + MERI = NewCode("MERI") + MTA = NewCode("MTA") + MBF = NewCode("MBF") + Y1D1S = NewCode("Y1D1S") + GMCOIN = NewCode("GMCOIN") + KISHU = NewCode("KISHU") + OPX = NewCode("OPX") + PCE = NewCode("PCE") + SFIL = NewCode("SFIL") + BID = NewCode("BID") + BKS = NewCode("BKS") + PIZA = NewCode("PIZA") + POSI = NewCode("POSI") + WSG = NewCode("WSG") + K21 = NewCode("K21") + PAI = NewCode("PAI") + HEX = NewCode("HEX") + YFFII = NewCode("YFFII") + IMC = NewCode("IMC") + ONES = NewCode("ONES") + CRB = NewCode("CRB") + DBA = NewCode("DBA") + SEAD = NewCode("SEAD") + SYN = NewCode("SYN") + TAC = NewCode("TAC") + DAX = NewCode("DAX") + LFIL12 = NewCode("LFIL12") + LFW = NewCode("LFW") + TYB = NewCode("TYB") + FCL = NewCode("FCL") + GERA = NewCode("GERA") + LID = NewCode("LID") + TT = NewCode("TT") + WELL = NewCode("WELL") + GALT = NewCode("GALT") + GMC = NewCode("GMC") + BPX = NewCode("BPX") + DOE = NewCode("DOE") + REVV = NewCode("REVV") + VTT = NewCode("VTT") + MAI = NewCode("MAI") + PDF = NewCode("PDF") + SEER = NewCode("SEER") + GFI = NewCode("GFI") + GODS = NewCode("GODS") + FISH = NewCode("FISH") + MIST = NewCode("MIST") + SEOS = NewCode("SEOS") + AWF = NewCode("AWF") + DADDYDOGE = NewCode("DADDYDOGE") + MNSTRS = NewCode("MNSTRS") + TREE = NewCode("TREE") + BNX = NewCode("BNX") + DESIRE = NewCode("DESIRE") + FIC24 = NewCode("FIC24") + RYOSHI = NewCode("RYOSHI") + TABOO = NewCode("TABOO") + CMCX = NewCode("CMCX") + CRE = NewCode("CRE") + FIL6 = NewCode("FIL6") + HTMOON = NewCode("HTMOON") + PORNROCKET = NewCode("PORNROCKET") + QUID = NewCode("QUID") + SAIT = NewCode("SAIT") + TGC = NewCode("TGC") + CVA = NewCode("CVA") + EMAX = NewCode("EMAX") + XDOGE = NewCode("XDOGE") + TUBE2 = NewCode("TUBE2") + TZKI = NewCode("TZKI") + YOOSHI = NewCode("YOOSHI") + GLEEC = NewCode("GLEEC") + PNT = NewCode("PNT") + UMI = NewCode("UMI") + DALI = NewCode("DALI") + DUKE = NewCode("DUKE") + MLTPX = NewCode("MLTPX") + CHE = NewCode("CHE") + KING = NewCode("KING") + MEWTWO = NewCode("MEWTWO") + SEED = NewCode("SEED") + DEKU = NewCode("DEKU") + FSHIB = NewCode("FSHIB") + MFLOKIADA = NewCode("MFLOKIADA") + MNI = NewCode("MNI") + NBL = NewCode("NBL") + POVE = NewCode("POVE") + SMTY = NewCode("SMTY") + CPH = NewCode("CPH") + FLM = NewCode("FLM") + GAT = NewCode("GAT") + MONONOKEINU = NewCode("MONONOKEINU") + SBR = NewCode("SBR") + BMARS = NewCode("BMARS") + GOMI = NewCode("GOMI") + ONOT = NewCode("ONOT") // nolint:misspell // false positive + GOKU = NewCode("GOKU") + MINTYS = NewCode("MINTYS") + PONYO = NewCode("PONYO") + WZC = NewCode("WZC") + ELAMA = NewCode("ELAMA") + NAMI = NewCode("NAMI") + SLINK = NewCode("SLINK") + SQUID = NewCode("SQUID") + DOGEZILLA = NewCode("DOGEZILLA") + INSUR = NewCode("INSUR") + IDA = NewCode("IDA") + MDX1 = NewCode("MDX1") + TRR = NewCode("TRR") + DXN = NewCode("DXN") + FCH = NewCode("FCH") + KAWA = NewCode("KAWA") + MCB = NewCode("MCB") + NABOX = NewCode("NABOX") + WANA = NewCode("WANA") + DOGECOLA = NewCode("DOGECOLA") + ELONGATE = NewCode("ELONGATE") + TNS = NewCode("TNS") + LEAD = NewCode("LEAD") + SYBC = NewCode("SYBC") + WINRY = NewCode("WINRY") + DAWGS = NewCode("DAWGS") + SMOON = NewCode("SMOON") + FIL36 = NewCode("FIL36") + KDS = NewCode("KDS") + SHR = NewCode("SHR") + BTY = NewCode("BTY") + FODL = NewCode("FODL") + XIASI = NewCode("XIASI") + RVST = NewCode("RVST") + VO = NewCode("VO") + GDR = NewCode("GDR") + RELCOIN = NewCode("RELCOIN") + CISLA = NewCode("CISLA") + ECOP = NewCode("ECOP") + AXSOLD = NewCode("AXSOLD") + BETA = NewCode("BETA") + BLINK = NewCode("BLINK") + PORTO = NewCode("PORTO") + SPARTAOLD = NewCode("SPARTAOLD") + WNXM = NewCode("WNXM") + ASR = NewCode("ASR") + COVEROLD = NewCode("COVEROLD") + VRAB = NewCode("VRAB") + NSBT = NewCode("NSBT") + AGIX = NewCode("AGIX") + BOLT = NewCode("BOLT") + BIDR = NewCode("BIDR") + VAB = NewCode("VAB") + EOSBULL = NewCode("EOSBULL") + FIO = NewCode("FIO") + IDEX = NewCode("IDEX") + PROS = NewCode("PROS") + VITE = NewCode("VITE") + WSOL = NewCode("WSOL") + FIRO = NewCode("FIRO") + MTLX = NewCode("MTLX") + SLPOLD = NewCode("SLPOLD") + WING = NewCode("WING") + SPARTA = NewCode("SPARTA") + USDS = NewCode("USDS") + BNC = NewCode("BNC") + BEAR = NewCode("BEAR") + OG = NewCode("OG") + TKO = NewCode("TKO") + UFT = NewCode("UFT") + SNMOLD = NewCode("SNMOLD") + WRX = NewCode("WRX") + BKRW = NewCode("BKRW") + BNBBULL = NewCode("BNBBULL") + PERLOLD = NewCode("PERLOLD") + BOBA = NewCode("BOBA") + COCOS = NewCode("COCOS") + NVT = NewCode("NVT") + TBCC = NewCode("TBCC") + BTCST = NewCode("BTCST") + DEXE = NewCode("DEXE") + HARD = NewCode("HARD") + DREPOLD = NewCode("DREPOLD") + UND = NewCode("UND") + XDATA = NewCode("XDATA") + KEYFI = NewCode("KEYFI") + MA = NewCode("MA") + QI = NewCode("QI") + ACA = NewCode("ACA") + DF = NewCode("DF") + KNCL = NewCode("KNCL") + BVND = NewCode("BVND") + PERL = NewCode("PERL") + WETH = NewCode("WETH") + BETH = NewCode("BETH") + OM = NewCode("OM") + OMOLD = NewCode("OMOLD") + PHB = NewCode("PHB") + ASTR = NewCode("ASTR") + HNST = NewCode("HNST") + JEX = NewCode("JEX") + ZCX = NewCode("ZCX") + DAR = NewCode("DAR") + MDXT = NewCode("MDXT") + RENBTC = NewCode("RENBTC") + SSV = NewCode("SSV") + XRPBEAR = NewCode("XRPBEAR") + AVA = NewCode("AVA") + SGT = NewCode("SGT") + VGX = NewCode("VGX") + EASY = NewCode("EASY") + IRIS = NewCode("IRIS") + VRT = NewCode("VRT") + WBNB = NewCode("WBNB") + DON = NewCode("DON") + JUV = NewCode("JUV") + PHA = NewCode("PHA") + QISWAP = NewCode("QISWAP") + SUNOLD = NewCode("SUNOLD") + ETHBEAR = NewCode("ETHBEAR") + FRONT = NewCode("FRONT") + LAZIO = NewCode("LAZIO") + BCHSV = NewCode("BCHSV") + EPS = NewCode("EPS") + ETHBNT = NewCode("ETHBNT") + HBAR = NewCode("HBAR") + ACM = NewCode("ACM") + CBM = NewCode("CBM") + DREP = NewCode("DREP") + ERD = NewCode("ERD") + STMX = NewCode("STMX") + ANTOLD = NewCode("ANTOLD") + BULL = NewCode("BULL") + BNBBEAR = NewCode("BNBBEAR") + CITY = NewCode("CITY") + AKRO = NewCode("AKRO") + ENTRP = NewCode("ENTRP") + REPV1 = NewCode("REPV1") + VIDT = NewCode("VIDT") + BGBP = NewCode("BGBP") + LOOMOLD = NewCode("LOOMOLD") + MBOX = NewCode("MBOX") + ADXOLD = NewCode("ADXOLD") + IDRT = NewCode("IDRT") + PHBV1 = NewCode("PHBV1") + FRAX = NewCode("FRAX") + LUSD = NewCode("LUSD") + OUSD = NewCode("OUSD") + USDX = NewCode("USDX") + EURS = NewCode("EURS") + CUSD = NewCode("CUSD") + MUSD = NewCode("MUSD") + USDK = NewCode("USDK") + EOSDT = NewCode("EOSDT") + DGX = NewCode("DGX") + XCHF = NewCode("XCHF") + XAUR = NewCode("XAUR") + USNBT = NewCode("USNBT") + ITL = NewCode("ITL") + MIM = NewCode("MIM") + ALUSD = NewCode("ALUSD") + BRCP = NewCode("BRCP") + USDs = NewCode("USDs") + MTR = NewCode("MTR") + CEUR = NewCode("CEUR") + ONEGOLD = NewCode("1GOLD") + COFFIN = NewCode("COFFIN") + MDO = NewCode("MDO") + DPT = NewCode("DPT") + XIDR = NewCode("XIDR") + PAR = NewCode("PAR") + XUSD = NewCode("XUSD") + USDB = NewCode("USDB") + USDQ = NewCode("USDQ") + BITUSD = NewCode("BITUSD") + BITGOLD = NewCode("BITGOLD") + BITEUR = NewCode("BITEUR") + HGT = NewCode("HGT") + CONST = NewCode("CONST") + XEUR = NewCode("XEUR") + EBASE = NewCode("EBASE") + USDL = NewCode("USDL") + UETH = NewCode("UETH") + USDEX = NewCode("USDEX") + USDFL = NewCode("USDFL") + FLUSD = NewCode("FLUSD") + DUSD = NewCode("DUSD") + + stables = Currencies{ + USDT, + USDC, + BUSD, + UST, + DAI, + TUSD, + USDP, + USDN, + FEI, + TRIBE, + RSR, + FRAX, + LUSD, + HUSD, + OUSD, + XSGD, + GUSD, + USDX, + EURS, + CUSD, + SUSD, + QC, + VAI, + SBD, + DGD, + MUSD, + RSV, + USDK, + IDRT, + BITCNY, + EOSDT, + DGX, + XCHF, + XAUR, + USDS, + USNBT, + ITL, + MIM, + USDP, + EURT, + ALUSD, + BRCP, + TRYB, + USDs, + MTR, + CEUR, + ONEGOLD, + COFFIN, + MDO, + DPT, + MDS, + XIDR, + PAR, + XUSD, + USDB, + USDQ, + KBC, + ZUSD, + BITUSD, + BITGOLD, + BITEUR, + HGT, + CONST, + XEUR, + BGBP, + EBASE, + BKRW, + USDL, + UETH, + BVND, + USDEX, + USDFL, + FLUSD, + DUSD, + } ) diff --git a/currency/coinmarketcap/coinmarketcap.go b/currency/coinmarketcap/coinmarketcap.go index 2189823f94f..980233eee01 100644 --- a/currency/coinmarketcap/coinmarketcap.go +++ b/currency/coinmarketcap/coinmarketcap.go @@ -19,6 +19,16 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) +// NewFromSettings returns a new coin market cap instance with supplied settings +func NewFromSettings(cfg Settings) (*Coinmarketcap, error) { + c := &Coinmarketcap{} + c.SetDefaults() + if err := c.Setup(cfg); err != nil { + return nil, err + } + return c, nil +} + // SetDefaults sets default values for the exchange func (c *Coinmarketcap) SetDefaults() { c.Name = "CoinMarketCap" @@ -26,10 +36,14 @@ func (c *Coinmarketcap) SetDefaults() { c.Verbose = false c.APIUrl = baseURL c.APIVersion = version - c.Requester = request.New(c.Name, + var err error + c.Requester, err = request.New(c.Name, common.NewHTTPClientWithTimeout(defaultTimeOut), request.WithLimiter(request.NewBasicRateLimit(RateInterval, BasicRequestRate)), ) + if err != nil { + log.Errorln(log.Global, err) + } } // Setup sets user configuration @@ -41,7 +55,7 @@ func (c *Coinmarketcap) Setup(conf Settings) error { c.Enabled = true c.Verbose = conf.Verbose - c.APIkey = conf.APIkey + c.APIkey = conf.APIKey return c.SetAccountPlan(conf.AccountPlan) } @@ -714,7 +728,7 @@ func (c *Coinmarketcap) SetAccountPlan(s string) error { case "enterprise": c.Plan = Enterprise default: - log.Warnf(log.Global, "account plan %s not found, defaulting to basic", s) + log.Warnf(log.Currency, "account plan %s not found, defaulting to basic", s) c.Plan = Basic } return nil diff --git a/currency/coinmarketcap/coinmarketcap_test.go b/currency/coinmarketcap/coinmarketcap_test.go index 93d54c5a596..9779c96848e 100644 --- a/currency/coinmarketcap/coinmarketcap_test.go +++ b/currency/coinmarketcap/coinmarketcap_test.go @@ -36,7 +36,7 @@ func TestSetup(t *testing.T) { c.SetDefaults() cfg := Settings{} - cfg.APIkey = apikey + cfg.APIKey = apikey cfg.AccountPlan = apiAccountPlanLevel cfg.Enabled = true cfg.AccountPlan = "basic" diff --git a/currency/coinmarketcap/coinmarketcap_types.go b/currency/coinmarketcap/coinmarketcap_types.go index aa80033d9ad..333444b7263 100644 --- a/currency/coinmarketcap/coinmarketcap_types.go +++ b/currency/coinmarketcap/coinmarketcap_types.go @@ -70,7 +70,7 @@ type Settings struct { Name string `json:"name"` Enabled bool `json:"enabled"` Verbose bool `json:"verbose"` - APIkey string `json:"apiKey"` + APIKey string `json:"apiKey"` AccountPlan string `json:"accountPlan"` } diff --git a/currency/conversion.go b/currency/conversion.go index edeec92823d..a0361e9a9d7 100644 --- a/currency/conversion.go +++ b/currency/conversion.go @@ -19,9 +19,6 @@ type ConversionRates struct { func (c *ConversionRates) HasData() bool { c.mtx.Lock() defer c.mtx.Unlock() - if c.m == nil { - return false - } return len(c.m) != 0 } @@ -105,7 +102,7 @@ func (c *ConversionRates) Update(m map[string]float64) error { log.Debugln(log.Global, "Conversion rates are being updated.") } - solidvalues := make(map[Code]map[Code]float64) + solidvalues := make(map[*Item]map[*Item]float64) var list []Code // Verification list, cross check all currencies coming in @@ -113,26 +110,26 @@ func (c *ConversionRates) Update(m map[string]float64) error { for key, val := range m { code1 := storage.ValidateFiatCode(key[:3]) - if mainBaseCurrency == (Code{}) { + if mainBaseCurrency.Equal(EMPTYCODE) { mainBaseCurrency = code1 } code2 := storage.ValidateFiatCode(key[3:]) - if code1 == code2 { // Get rid of same conversions + if code1.Equal(code2) { // Get rid of same conversions continue } var codeOneFound, codeTwoFound bool // Check and add to our funky list for i := range list { - if list[i] == code1 { + if list[i].Equal(code1) { codeOneFound = true if codeTwoFound { break } } - if list[i] == code2 { + if list[i].Equal(code2) { codeTwoFound = true if codeOneFound { break @@ -148,39 +145,39 @@ func (c *ConversionRates) Update(m map[string]float64) error { list = append(list, code2) } - if solidvalues[code1] == nil { - solidvalues[code1] = make(map[Code]float64) + if solidvalues[code1.Item] == nil { + solidvalues[code1.Item] = make(map[*Item]float64) } - solidvalues[code1][code2] = val + solidvalues[code1.Item][code2.Item] = val // Input inverse values 1/val to swap from -> to and vice versa - if solidvalues[code2] == nil { - solidvalues[code2] = make(map[Code]float64) + if solidvalues[code2.Item] == nil { + solidvalues[code2.Item] = make(map[*Item]float64) } - solidvalues[code2][code1] = 1 / val + solidvalues[code2.Item][code1.Item] = 1 / val } for _, base := range list { for _, term := range list { - if base == term { + if base.Equal(term) { continue } - _, ok := solidvalues[base][term] + _, ok := solidvalues[base.Item][term.Item] if !ok { var crossRate float64 // Check inversion to speed things up - v, ok := solidvalues[term][base] + v, ok := solidvalues[term.Item][base.Item] if !ok { - v1, ok := solidvalues[mainBaseCurrency][base] + v1, ok := solidvalues[mainBaseCurrency.Item][base.Item] if !ok { return fmt.Errorf("value not found base %s term %s", mainBaseCurrency, base) } - v2, ok := solidvalues[mainBaseCurrency][term] + v2, ok := solidvalues[mainBaseCurrency.Item][term.Item] if !ok { return fmt.Errorf("value not found base %s term %s", mainBaseCurrency, @@ -197,7 +194,7 @@ func (c *ConversionRates) Update(m map[string]float64) error { term, crossRate) } - solidvalues[base][term] = crossRate + solidvalues[base.Item][term.Item] = crossRate } } } @@ -209,14 +206,14 @@ func (c *ConversionRates) Update(m map[string]float64) error { c.m = make(map[*Item]map[*Item]*float64) } - if c.m[key.Item] == nil { - c.m[key.Item] = make(map[*Item]*float64) + if c.m[key] == nil { + c.m[key] = make(map[*Item]*float64) } - p := c.m[key.Item][key2.Item] + p := c.m[key][key2] if p == nil { newPalsAndFriends := val2 - c.m[key.Item][key2.Item] = &newPalsAndFriends + c.m[key][key2] = &newPalsAndFriends } else { *p = val2 } @@ -281,7 +278,7 @@ func (c Conversion) IsInvalid() bool { // IsFiat checks to see if the from and to currency is a fiat e.g. EURUSD func (c Conversion) IsFiat() bool { - return storage.IsFiatCurrency(c.From) && storage.IsFiatCurrency(c.To) + return c.From.IsFiatCurrency() && c.To.IsFiatCurrency() } // String returns the stringed fields diff --git a/currency/currencies.go b/currency/currencies.go index caa6cb9737e..013b7223924 100644 --- a/currency/currencies.go +++ b/currency/currencies.go @@ -7,7 +7,7 @@ import ( // NewCurrenciesFromStringArray returns a Currencies object from strings func NewCurrenciesFromStringArray(currencies []string) Currencies { - var list Currencies + list := make(Currencies, 0, len(currencies)) for i := range currencies { if currencies[i] == "" { continue @@ -22,17 +22,17 @@ type Currencies []Code // Strings returns an array of currency strings func (c Currencies) Strings() []string { - var list []string + list := make([]string, len(c)) for i := range c { - list = append(list, c[i].String()) + list[i] = c[i].String() } return list } // Contains checks to see if a currency code is contained in the currency list -func (c Currencies) Contains(cc Code) bool { +func (c Currencies) Contains(check Code) bool { for i := range c { - if c[i].Item == cc.Item { + if c[i].Equal(check) { return true } } @@ -52,10 +52,10 @@ func (c *Currencies) UnmarshalJSON(d []byte) error { return err } - var allTheCurrencies Currencies curr := strings.Split(configCurrencies, ",") + allTheCurrencies := make(Currencies, len(curr)) for i := range curr { - allTheCurrencies = append(allTheCurrencies, NewCode(curr[i])) + allTheCurrencies[i] = NewCode(curr[i]) } *c = allTheCurrencies @@ -76,7 +76,7 @@ func (c Currencies) Match(other Currencies) bool { match: for x := range c { for y := range other { - if c[x] == other[y] { + if c[x].Equal(other[y]) { continue match } } diff --git a/currency/currency.go b/currency/currency.go index 24b597860d3..4e3ff95f7d5 100644 --- a/currency/currency.go +++ b/currency/currency.go @@ -1,5 +1,13 @@ package currency +import ( + "errors" + "fmt" + "strings" +) + +var errEmptyPairString = errors.New("empty pair string") + // GetDefaultExchangeRates returns the currency exchange rates based off the // default fiat values func GetDefaultExchangeRates() (Conversions, error) { @@ -56,11 +64,16 @@ func UpdateCurrencies(c Currencies, isCryptocurrency bool) { storage.UpdateEnabledFiatCurrencies(c) } -// ConvertCurrency converts an amount from one currency to another -func ConvertCurrency(amount float64, from, to Code) (float64, error) { +// ConvertFiat converts an fiat amount from one currency to another +func ConvertFiat(amount float64, from, to Code) (float64, error) { return storage.ConvertCurrency(amount, from, to) } +// GetForeignExchangeRate returns the foreign exchange rate for a fiat pair. +func GetForeignExchangeRate(quotation Pair) (float64, error) { + return storage.ConvertCurrency(1, quotation.Base, quotation.Quote) +} + // SeedForeignExchangeData seeds FX data with the currencies supplied func SeedForeignExchangeData(c Currencies) error { return storage.SeedForeignExchangeRatesByCurrencies(c) @@ -72,7 +85,7 @@ func GetTotalMarketCryptocurrencies() ([]Code, error) { } // RunStorageUpdater runs a new foreign exchange updater instance -func RunStorageUpdater(o BotOverrides, m *MainConfiguration, filepath string) error { +func RunStorageUpdater(o BotOverrides, m *Config, filepath string) error { return storage.RunUpdater(o, m, filepath) } @@ -88,43 +101,47 @@ func CopyPairFormat(p Pair, pairs []Pair, exact bool) Pair { if p.Equal(pairs[x]) { return pairs[x] } + continue } if p.EqualIncludeReciprocal(pairs[x]) { return pairs[x] } } - return Pair{} + return EMPTYPAIR } // FormatPairs formats a string array to a list of currency pairs with the // supplied currency pair format func FormatPairs(pairs []string, delimiter, index string) (Pairs, error) { - var result Pairs + var result = make(Pairs, len(pairs)) for x := range pairs { if pairs[x] == "" { - continue + return nil, fmt.Errorf("%w in slice %v", errEmptyPairString, pairs) } - var p Pair var err error - if delimiter != "" { - p, err = NewPairDelimiter(pairs[x], delimiter) - if err != nil { - return nil, err - } - } else { - if index != "" { - p, err = NewPairFromIndex(pairs[x], index) - if err != nil { - return Pairs{}, err - } - } else { - p, err = NewPairFromStrings(pairs[x][0:3], pairs[x][3:]) - if err != nil { - return Pairs{}, err - } - } + switch { + case delimiter != "": + result[x], err = NewPairDelimiter(pairs[x], delimiter) + case index != "": + result[x], err = NewPairFromIndex(pairs[x], index) + default: + result[x], err = NewPairFromStrings(pairs[x][:3], pairs[x][3:]) + } + if err != nil { + return nil, err } - result = append(result, p) } return result, nil } + +// IsEnabled returns if the individual foreign exchange config setting is +// enabled +func (settings AllFXSettings) IsEnabled(name string) bool { + for x := range settings { + if !strings.EqualFold(settings[x].Name, name) { + continue + } + return settings[x].Enabled + } + return false +} diff --git a/currency/currency_test.go b/currency/currency_test.go index b726c8622a2..b524fc4b18d 100644 --- a/currency/currency_test.go +++ b/currency/currency_test.go @@ -1,6 +1,7 @@ package currency import ( + "errors" "testing" ) @@ -43,14 +44,14 @@ func TestUpdateBaseCurrency(t *testing.T) { t.Error("UpdateBaseCurrency() error cannot be nil") } - if GetBaseCurrency() != AUD { + if !GetBaseCurrency().Equal(AUD) { t.Errorf("GetBaseCurrency() expected %s but received %s", AUD, GetBaseCurrency()) } } func TestGetDefaultBaseCurrency(t *testing.T) { - if GetDefaultBaseCurrency() != USD { + if !GetDefaultBaseCurrency().Equal(USD) { t.Errorf("GetDefaultBaseCurrency() expected %s but received %s", USD, GetDefaultBaseCurrency()) } @@ -88,13 +89,28 @@ func TestUpdateCurrencies(t *testing.T) { } } -func TestConvertCurrency(t *testing.T) { - _, err := ConvertCurrency(100, AUD, USD) +func TestConvertFiat(t *testing.T) { + _, err := ConvertFiat(0, LTC, USD) + if !errors.Is(err, errInvalidAmount) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidAmount) + } + + _, err = ConvertFiat(100, LTC, USD) + if !errors.Is(err, errNotFiatCurrency) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency) + } + + _, err = ConvertFiat(100, USD, LTC) + if !errors.Is(err, errNotFiatCurrency) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency) + } + + _, err = ConvertFiat(100, AUD, USD) if err != nil { t.Fatal(err) } - r, err := ConvertCurrency(100, AUD, AUD) + r, err := ConvertFiat(100, AUD, AUD) if err != nil { t.Fatal(err) } @@ -104,23 +120,64 @@ func TestConvertCurrency(t *testing.T) { 100.00, r) } - _, err = ConvertCurrency(100, USD, AUD) + _, err = ConvertFiat(100, USD, AUD) if err != nil { t.Fatal(err) } - _, err = ConvertCurrency(100, CNY, AUD) + _, err = ConvertFiat(100, CNY, AUD) if err != nil { t.Fatal(err) } +} - _, err = ConvertCurrency(100, LTC, USD) - if err == nil { - t.Fatal("Expected err on non-existent currency") +func TestGetForeignExchangeRate(t *testing.T) { + _, err := GetForeignExchangeRate(NewPair(EMPTYCODE, EMPTYCODE)) + if !errors.Is(err, errNotFiatCurrency) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency) } - _, err = ConvertCurrency(100, USD, LTC) - if err == nil { - t.Fatal("Expected err on non-existent currency") + _, err = GetForeignExchangeRate(NewPair(USD, EMPTYCODE)) + if !errors.Is(err, errNotFiatCurrency) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency) + } + + one, err := GetForeignExchangeRate(NewPair(USD, USD)) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if one != 1 { + t.Fatal("unexpected value") + } + + rate, err := GetForeignExchangeRate(NewPair(AUD, USD)) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if rate <= 0 { + t.Fatal("unexpected value") + } +} + +func TestAllFXSettingsIsEnabled(t *testing.T) { + var settings AllFXSettings + if received := settings.IsEnabled("wow"); received { + t.Fatalf("received: '%v' but expected: '%v'", received, false) + } + + settings = []FXSettings{ + { + Name: "wOow", + }, + { + Name: "amICool?", + Enabled: true, + }, + } + + if received := settings.IsEnabled("AMICOOL?"); !received { + t.Fatalf("received: '%v' but expected: '%v'", received, true) } } diff --git a/currency/currency_types.go b/currency/currency_types.go index e3494d5c4cc..ced6f5bb908 100644 --- a/currency/currency_types.go +++ b/currency/currency_types.go @@ -6,26 +6,35 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency/coinmarketcap" ) -// MainConfiguration is the main configuration from the config.json file -type MainConfiguration struct { - ForexProviders []FXSettings - CryptocurrencyProvider coinmarketcap.Settings - Cryptocurrencies Currencies - CurrencyPairFormat interface{} - FiatDisplayCurrency Code - CurrencyDelay time.Duration - FxRateDelay time.Duration +// Config holds all the information needed for currency related manipulation +type Config struct { + ForexProviders AllFXSettings `json:"forexProviders"` + CryptocurrencyProvider Provider `json:"cryptocurrencyProvider"` + CurrencyPairFormat *PairFormat `json:"currencyPairFormat"` + FiatDisplayCurrency Code `json:"fiatDisplayCurrency"` + CurrencyFileUpdateDuration time.Duration `json:"currencyFileUpdateDuration"` + ForeignExchangeUpdateDuration time.Duration `json:"foreignExchangeUpdateDuration"` +} + +// Provider defines coinmarketcap tools +type Provider struct { + Name string `json:"name"` + Enabled bool `json:"enabled"` + Verbose bool `json:"verbose"` + APIKey string `json:"apiKey"` + AccountPlan string `json:"accountPlan"` } // BotOverrides defines a bot overriding factor for quick running currency // subsystems type BotOverrides struct { - Coinmarketcap bool - FxCurrencyConverter bool - FxCurrencyLayer bool - FxFixer bool - FxOpenExchangeRates bool - FxExchangeRateHost bool + Coinmarketcap bool + CurrencyConverter bool + CurrencyLayer bool + ExchangeRates bool + Fixer bool + OpenExchangeRates bool + ExchangeRateHost bool } // CoinmarketcapSettings refers to settings @@ -40,26 +49,29 @@ type SystemsSettings struct { Openexchangerates FXSettings } +// AllFXSettings defines all the foreign exchange settings +type AllFXSettings []FXSettings + // FXSettings defines foreign exchange requester settings type FXSettings struct { - Name string `json:"name"` - Enabled bool `json:"enabled"` - Verbose bool `json:"verbose"` - RESTPollingDelay time.Duration `json:"restPollingDelay"` - APIKey string `json:"apiKey"` - APIKeyLvl int `json:"apiKeyLvl"` - PrimaryProvider bool `json:"primaryProvider"` + Name string `json:"name"` + Enabled bool `json:"enabled"` + Verbose bool `json:"verbose"` + APIKey string `json:"apiKey"` + APIKeyLvl int `json:"apiKeyLvl"` + PrimaryProvider bool `json:"primaryProvider"` } // File defines a full currency file generated by the currency storage // analysis system type File struct { LastMainUpdate interface{} `json:"lastMainUpdate"` - Cryptocurrency []Item `json:"cryptocurrencies"` - FiatCurrency []Item `json:"fiatCurrencies"` - UnsetCurrency []Item `json:"unsetCurrencies"` - Contracts []Item `json:"contracts"` - Token []Item `json:"tokens"` + Cryptocurrency []*Item `json:"cryptocurrencies"` + FiatCurrency []*Item `json:"fiatCurrencies"` + UnsetCurrency []*Item `json:"unsetCurrencies"` + Contracts []*Item `json:"contracts"` + Token []*Item `json:"tokens"` + Stable []*Item `json:"stableCurrencies"` } // Const here are packaged defined delimiters diff --git a/currency/forexprovider/README.md b/currency/forexprovider/README.md index 930bff13ea2..25118167e2d 100644 --- a/currency/forexprovider/README.md +++ b/currency/forexprovider/README.md @@ -22,6 +22,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader + Currency Converter API support + Currency Layer support ++ Exchange Rates support + Fixer.io support + Open Exchange Rates support + ExchangeRate.host support diff --git a/currency/forexprovider/base/base.go b/currency/forexprovider/base/base.go index 82ac314abb0..935a76bcbc5 100644 --- a/currency/forexprovider/base/base.go +++ b/currency/forexprovider/base/base.go @@ -29,11 +29,10 @@ const DefaultTimeOut = time.Second * 15 // Settings enforces standard variables across the provider packages type Settings struct { - Name string `json:"name"` - Enabled bool `json:"enabled"` - Verbose bool `json:"verbose"` - RESTPollingDelay time.Duration `json:"restPollingDelay"` - APIKey string `json:"apiKey"` - APIKeyLvl int `json:"apiKeyLvl"` - PrimaryProvider bool `json:"primaryProvider"` + Name string `json:"name"` + Enabled bool `json:"enabled"` + Verbose bool `json:"verbose"` + APIKey string `json:"apiKey"` + APIKeyLvl int `json:"apiKeyLvl"` + PrimaryProvider bool `json:"primaryProvider"` } diff --git a/currency/forexprovider/currencyconverterapi/currencyconverterapi.go b/currency/forexprovider/currencyconverterapi/currencyconverterapi.go index f5340cb2bc3..5e65e3df575 100644 --- a/currency/forexprovider/currencyconverterapi/currencyconverterapi.go +++ b/currency/forexprovider/currencyconverterapi/currencyconverterapi.go @@ -21,13 +21,13 @@ func (c *CurrencyConverter) Setup(config base.Settings) error { c.APIKey = config.APIKey c.APIKeyLvl = config.APIKeyLvl c.Enabled = config.Enabled - c.RESTPollingDelay = config.RESTPollingDelay c.Verbose = config.Verbose c.PrimaryProvider = config.PrimaryProvider - c.Requester = request.New(c.Name, + var err error + c.Requester, err = request.New(c.Name, common.NewHTTPClientWithTimeout(base.DefaultTimeOut), request.WithLimiter(request.NewBasicRateLimit(rateInterval, requestRate))) - return nil + return err } // GetRates is a wrapper function to return rates diff --git a/currency/forexprovider/currencylayer/currencylayer.go b/currency/forexprovider/currencylayer/currencylayer.go index f9e127bb9c8..5fb5cceae4d 100644 --- a/currency/forexprovider/currencylayer/currencylayer.go +++ b/currency/forexprovider/currencylayer/currencylayer.go @@ -40,14 +40,13 @@ func (c *CurrencyLayer) Setup(config base.Settings) error { c.APIKey = config.APIKey c.APIKeyLvl = config.APIKeyLvl c.Enabled = config.Enabled - c.RESTPollingDelay = config.RESTPollingDelay c.Verbose = config.Verbose c.PrimaryProvider = config.PrimaryProvider // Rate limit is based off a monthly counter - Open limit used. - c.Requester = request.New(c.Name, + var err error + c.Requester, err = request.New(c.Name, common.NewHTTPClientWithTimeout(base.DefaultTimeOut)) - - return nil + return err } // GetRates is a wrapper function to return rates for GoCryptoTrader diff --git a/currency/forexprovider/currencylayer/currencylayer_types.go b/currency/forexprovider/currencylayer/currencylayer_types.go index 167531232b9..43848c7bc98 100644 --- a/currency/forexprovider/currencylayer/currencylayer_types.go +++ b/currency/forexprovider/currencylayer/currencylayer_types.go @@ -20,9 +20,6 @@ const ( APIEndpointConversion = "convert" APIEndpointTimeframe = "timeframe" APIEndpointChange = "change" - - authRate = 0 - unAuthRate = 0 ) // CurrencyLayer is a foreign exchange rate provider at diff --git a/currency/forexprovider/exchangerate.host/exchangerate.go b/currency/forexprovider/exchangerate.host/exchangerate.go index cc2314de4d7..e4b78bd9424 100644 --- a/currency/forexprovider/exchangerate.host/exchangerate.go +++ b/currency/forexprovider/exchangerate.host/exchangerate.go @@ -31,12 +31,12 @@ var ( func (e *ExchangeRateHost) Setup(config base.Settings) error { e.Name = config.Name e.Enabled = config.Enabled - e.RESTPollingDelay = config.RESTPollingDelay e.Verbose = config.Verbose e.PrimaryProvider = config.PrimaryProvider - e.Requester = request.New(e.Name, + var err error + e.Requester, err = request.New(e.Name, common.NewHTTPClientWithTimeout(base.DefaultTimeOut)) - return nil + return err } // GetLatestRates returns a list of forex rates based on the supplied params diff --git a/currency/forexprovider/exchangeratesapi.io/exchangeratesapi.go b/currency/forexprovider/exchangeratesapi.io/exchangeratesapi.go index 1d7f2d537ea..77f565c07e3 100644 --- a/currency/forexprovider/exchangeratesapi.io/exchangeratesapi.go +++ b/currency/forexprovider/exchangeratesapi.io/exchangeratesapi.go @@ -25,15 +25,15 @@ func (e *ExchangeRates) Setup(config base.Settings) error { } e.Name = config.Name e.Enabled = config.Enabled - e.RESTPollingDelay = config.RESTPollingDelay e.Verbose = config.Verbose e.PrimaryProvider = config.PrimaryProvider e.APIKey = config.APIKey e.APIKeyLvl = config.APIKeyLvl - e.Requester = request.New(e.Name, + var err error + e.Requester, err = request.New(e.Name, common.NewHTTPClientWithTimeout(base.DefaultTimeOut), request.WithLimiter(request.NewBasicRateLimit(rateLimitInterval, requestRate))) - return nil + return err } func (e *ExchangeRates) cleanCurrencies(baseCurrency, symbols string) string { diff --git a/currency/forexprovider/fixer.io/fixer.go b/currency/forexprovider/fixer.io/fixer.go index b4e22941a89..8730fc08b05 100644 --- a/currency/forexprovider/fixer.io/fixer.go +++ b/currency/forexprovider/fixer.io/fixer.go @@ -34,12 +34,12 @@ func (f *Fixer) Setup(config base.Settings) error { f.APIKeyLvl = config.APIKeyLvl f.Enabled = config.Enabled f.Name = config.Name - f.RESTPollingDelay = config.RESTPollingDelay f.Verbose = config.Verbose f.PrimaryProvider = config.PrimaryProvider - f.Requester = request.New(f.Name, + var err error + f.Requester, err = request.New(f.Name, common.NewHTTPClientWithTimeout(base.DefaultTimeOut)) - return nil + return err } // GetSupportedCurrencies returns supported currencies @@ -220,10 +220,13 @@ func (f *Fixer) GetFluctuationData(startDate, endDate, baseCurrency string, symb // SendOpenHTTPRequest sends a typical get request func (f *Fixer) SendOpenHTTPRequest(endpoint string, v url.Values, result interface{}) error { - var path string + if v == nil { + v = url.Values{} + } v.Set("access_key", f.APIKey) var auth bool + var path string if f.APIKeyLvl == fixerAPIFree { path = fixerAPI + endpoint + "?" + v.Encode() } else { diff --git a/currency/forexprovider/fixer.io/fixer_test.go b/currency/forexprovider/fixer.io/fixer_test.go index ecfc366ad81..49a5ae4eb39 100644 --- a/currency/forexprovider/fixer.io/fixer_test.go +++ b/currency/forexprovider/fixer.io/fixer_test.go @@ -9,11 +9,6 @@ import ( // Please set API key and apikey subscription level for correct due diligence // testing - NOTE please be aware tests will diminish your monthly API calls -const ( - apikey = "" - apiKeyLvl = 3 -) - var f Fixer var isSetup bool diff --git a/currency/forexprovider/forexprovider.go b/currency/forexprovider/forexprovider.go index 6643d3ba45c..393a01fabd4 100644 --- a/currency/forexprovider/forexprovider.go +++ b/currency/forexprovider/forexprovider.go @@ -4,6 +4,7 @@ package forexprovider import ( "errors" + "fmt" "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base" currencyconverter "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencyconverterapi" @@ -14,6 +15,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/openexchangerates" ) +var ( + errUnhandledForeignExchangeProvider = errors.New("unhandled foreign exchange provider") + errNoPrimaryForexProviderEnabled = errors.New("no primary forex provider enabled") +) + // ForexProviders is a foreign exchange handler type type ForexProviders struct { base.FXHandler @@ -83,69 +89,37 @@ func (f *ForexProviders) SetProvider(b base.IFXProvider) error { // StartFXService starts the forex provider service and returns a pointer to it func StartFXService(fxProviders []base.Settings) (*ForexProviders, error) { handler := new(ForexProviders) - for i := range fxProviders { - switch { - case fxProviders[i].Name == "CurrencyConverter" && fxProviders[i].Enabled: - provider := new(currencyconverter.CurrencyConverter) - err := provider.Setup(fxProviders[i]) - if err != nil { - return nil, err - } - - err = handler.SetProvider(provider) - if err != nil { - return nil, err - } - case fxProviders[i].Name == "CurrencyLayer" && fxProviders[i].Enabled: - provider := new(currencylayer.CurrencyLayer) - err := provider.Setup(fxProviders[i]) - if err != nil { - return nil, err - } - - err = handler.SetProvider(provider) - if err != nil { - return nil, err - } - case fxProviders[i].Name == "ExchangeRates" && fxProviders[i].Enabled: - provider := new(exchangerates.ExchangeRates) - err := provider.Setup(fxProviders[i]) - if err != nil { - return nil, err - } - - err = handler.SetProvider(provider) - if err != nil { - return nil, err - } - case fxProviders[i].Name == "Fixer" && fxProviders[i].Enabled: - provider := new(fixer.Fixer) - err := provider.Setup(fxProviders[i]) - if err != nil { - return nil, err - } - - err = handler.SetProvider(provider) - if err != nil { - return nil, err - } - case fxProviders[i].Name == "OpenExchangeRates" && fxProviders[i].Enabled: - provider := new(openexchangerates.OXR) - err := provider.Setup(fxProviders[i]) - if err != nil { - return nil, err - } - - err = handler.SetProvider(provider) - if err != nil { - return nil, err - } + var provider base.IFXProvider + switch fxProviders[i].Name { + case "CurrencyConverter": + provider = new(currencyconverter.CurrencyConverter) + case "CurrencyLayer": + provider = new(currencylayer.CurrencyLayer) + case "ExchangeRates": + provider = new(exchangerates.ExchangeRates) + case "Fixer": + provider = new(fixer.Fixer) + case "OpenExchangeRates": + provider = new(openexchangerates.OXR) + case "ExchangeRateHost": + provider = new(exchangeratehost.ExchangeRateHost) + default: + return nil, fmt.Errorf("%s %w", fxProviders[i].Name, + errUnhandledForeignExchangeProvider) + } + err := provider.Setup(fxProviders[i]) + if err != nil { + return nil, err + } + err = handler.SetProvider(provider) + if err != nil { + return nil, err } } if handler.Primary.Provider == nil { - return nil, errors.New("no primary forex provider enabled") + return nil, errNoPrimaryForexProviderEnabled } return handler, nil diff --git a/currency/forexprovider/openexchangerates/openexchangerates.go b/currency/forexprovider/openexchangerates/openexchangerates.go index 8132cf312c7..94473cce792 100644 --- a/currency/forexprovider/openexchangerates/openexchangerates.go +++ b/currency/forexprovider/openexchangerates/openexchangerates.go @@ -35,12 +35,12 @@ func (o *OXR) Setup(config base.Settings) error { o.APIKeyLvl = config.APIKeyLvl o.Enabled = config.Enabled o.Name = config.Name - o.RESTPollingDelay = config.RESTPollingDelay o.Verbose = config.Verbose o.PrimaryProvider = config.PrimaryProvider - o.Requester = request.New(o.Name, + var err error + o.Requester, err = request.New(o.Name, common.NewHTTPClientWithTimeout(base.DefaultTimeOut)) - return nil + return err } // GetRates is a wrapper function to return rates diff --git a/currency/manager.go b/currency/manager.go index 92534789057..246aaea0862 100644 --- a/currency/manager.go +++ b/currency/manager.go @@ -16,6 +16,12 @@ var ( ErrPairAlreadyEnabled = errors.New("pair already enabled") // ErrPairNotFound is returned when a currency pair is not found ErrPairNotFound = errors.New("pair not found") + // errAssetNotEnabled defines an error for the pairs management system + // that declares the asset is not enabled. + errAssetNotEnabled = errors.New("asset not enabled") + // ErrAssetIsNil is an error when the asset has not been populated by the + // configuration + ErrAssetIsNil = errors.New("asset is nil") ) // GetAssetTypes returns a list of stored asset types @@ -168,11 +174,11 @@ func (p *PairsManager) IsAssetEnabled(a asset.Item) error { } if c.AssetEnabled == nil { - return errors.New("cannot ascertain if asset is enabled, variable is nil") + return fmt.Errorf("%s %w", a, ErrAssetIsNil) } if !*c.AssetEnabled { - return fmt.Errorf("asset %s not enabled", a) + return fmt.Errorf("%s %w", a, errAssetNotEnabled) } return nil } diff --git a/currency/manager_test.go b/currency/manager_test.go index 00694d857bb..0ef8035243d 100644 --- a/currency/manager_test.go +++ b/currency/manager_test.go @@ -250,13 +250,13 @@ func TestDisablePair(t *testing.T) { // Test asset type which doesn't exist initTest(t) - if err := p.DisablePair(asset.Futures, Pair{}); err == nil { + if err := p.DisablePair(asset.Futures, EMPTYPAIR); err == nil { t.Error("unexpected result") } // Test asset type which has an empty pair store p.Pairs[asset.Spot] = nil - if err := p.DisablePair(asset.Spot, Pair{}); err == nil { + if err := p.DisablePair(asset.Spot, EMPTYPAIR); err == nil { t.Error("unexpected result") } @@ -281,13 +281,13 @@ func TestEnablePair(t *testing.T) { // Test asset type which doesn't exist initTest(t) - if err := p.EnablePair(asset.Futures, Pair{}); err == nil { + if err := p.EnablePair(asset.Futures, EMPTYPAIR); err == nil { t.Error("unexpected result") } // Test asset type which has an empty pair store p.Pairs[asset.Spot] = nil - if err := p.EnablePair(asset.Spot, Pair{}); err == nil { + if err := p.EnablePair(asset.Spot, EMPTYPAIR); err == nil { t.Error("unexpected result") } diff --git a/currency/pair.go b/currency/pair.go index cd800adeccf..13be4a0c8b6 100644 --- a/currency/pair.go +++ b/currency/pair.go @@ -1,20 +1,23 @@ package currency import ( + "errors" "fmt" "strings" ) +var errCannotCreatePair = errors.New("cannot create currency pair") + // NewPairDelimiter splits the desired currency string at delimeter, the returns // a Pair struct func NewPairDelimiter(currencyPair, delimiter string) (Pair, error) { if !strings.Contains(currencyPair, delimiter) { - return Pair{}, + return EMPTYPAIR, fmt.Errorf("delimiter: [%s] not found in currencypair string", delimiter) } result := strings.Split(currencyPair, delimiter) if len(result) < 2 { - return Pair{}, + return EMPTYPAIR, fmt.Errorf("supplied pair: [%s] cannot be split with %s", currencyPair, delimiter) @@ -32,13 +35,13 @@ func NewPairDelimiter(currencyPair, delimiter string) (Pair, error) { // NewPairFromStrings returns a CurrencyPair without a delimiter func NewPairFromStrings(base, quote string) (Pair, error) { if strings.Contains(base, " ") { - return Pair{}, + return EMPTYPAIR, fmt.Errorf("cannot create pair, invalid base currency string [%s]", base) } if strings.Contains(quote, " ") { - return Pair{}, + return EMPTYPAIR, fmt.Errorf("cannot create pair, invalid quote currency string [%s]", quote) } @@ -68,7 +71,7 @@ func NewPairWithDelimiter(base, quote, delimiter string) Pair { func NewPairFromIndex(currencyPair, index string) (Pair, error) { i := strings.Index(currencyPair, index) if i == -1 { - return Pair{}, + return EMPTYPAIR, fmt.Errorf("index %s not found in currency pair string", index) } if i == 0 { @@ -87,8 +90,9 @@ func NewPairFromString(currencyPair string) (Pair, error) { } } if len(currencyPair) < 3 { - return Pair{}, - fmt.Errorf("cannot create pair from %s string", + return EMPTYPAIR, + fmt.Errorf("%w from %s string too short to be a current pair", + errCannotCreatePair, currencyPair) } return NewPairFromStrings(currencyPair[0:3], currencyPair[3:]) @@ -100,8 +104,7 @@ func NewPairFromString(currencyPair string) (Pair, error) { // apply the same format func NewPairFromFormattedPairs(currencyPair string, pairs Pairs, pairFmt PairFormat) (Pair, error) { for x := range pairs { - fPair := pairFmt.Format(pairs[x]) - if strings.EqualFold(fPair, currencyPair) { + if strings.EqualFold(pairFmt.Format(pairs[x]), currencyPair) { return pairs[x], nil } } @@ -132,5 +135,5 @@ func MatchPairsWithNoDelimiter(currencyPair string, pairs Pairs, pairFmt PairFor } } } - return Pair{}, fmt.Errorf("currency %v not found in supplied pairs", currencyPair) + return EMPTYPAIR, fmt.Errorf("currency %v not found in supplied pairs", currencyPair) } diff --git a/currency/pair_methods.go b/currency/pair_methods.go index f377d07c97f..41460597574 100644 --- a/currency/pair_methods.go +++ b/currency/pair_methods.go @@ -2,7 +2,6 @@ package currency import ( "encoding/json" - "strings" ) // String returns a currency pair string @@ -12,20 +11,16 @@ func (p Pair) String() string { // Lower converts the pair object to lowercase func (p Pair) Lower() Pair { - return Pair{ - Delimiter: p.Delimiter, - Base: p.Base.Lower(), - Quote: p.Quote.Lower(), - } + p.Base = p.Base.Lower() + p.Quote = p.Quote.Lower() + return p } // Upper converts the pair object to uppercase func (p Pair) Upper() Pair { - return Pair{ - Delimiter: p.Delimiter, - Base: p.Base.Upper(), - Quote: p.Quote.Upper(), - } + p.Base = p.Base.Upper() + p.Quote = p.Quote.Upper() + return p } // UnmarshalJSON comforms type to the umarshaler interface @@ -41,9 +36,7 @@ func (p *Pair) UnmarshalJSON(d []byte) error { return err } - p.Base = newPair.Base - p.Quote = newPair.Quote - p.Delimiter = newPair.Delimiter + *p = newPair return nil } @@ -55,49 +48,56 @@ func (p Pair) MarshalJSON() ([]byte, error) { // Format changes the currency based on user preferences overriding the default // String() display func (p Pair) Format(delimiter string, uppercase bool) Pair { - newP := Pair{Base: p.Base, Quote: p.Quote, Delimiter: delimiter} + p.Delimiter = delimiter if uppercase { - return newP.Upper() + return p.Upper() } - return newP.Lower() + return p.Lower() } // Equal compares two currency pairs and returns whether or not they are equal func (p Pair) Equal(cPair Pair) bool { - return strings.EqualFold(p.Base.String(), cPair.Base.String()) && - strings.EqualFold(p.Quote.String(), cPair.Quote.String()) + return p.Base.Equal(cPair.Base) && p.Quote.Equal(cPair.Quote) } // EqualIncludeReciprocal compares two currency pairs and returns whether or not // they are the same including reciprocal currencies. func (p Pair) EqualIncludeReciprocal(cPair Pair) bool { - if (p.Base.Item == cPair.Base.Item && p.Quote.Item == cPair.Quote.Item) || - (p.Base.Item == cPair.Quote.Item && p.Quote.Item == cPair.Base.Item) { - return true - } - return false + return (p.Base.Equal(cPair.Base) && p.Quote.Equal(cPair.Quote)) || + (p.Base.Equal(cPair.Quote) && p.Quote.Equal(cPair.Base)) } // IsCryptoPair checks to see if the pair is a crypto pair e.g. BTCLTC func (p Pair) IsCryptoPair() bool { - return storage.IsCryptocurrency(p.Base) && - storage.IsCryptocurrency(p.Quote) + return p.Base.IsCryptocurrency() && p.Quote.IsCryptocurrency() } // IsCryptoFiatPair checks to see if the pair is a crypto fiat pair e.g. BTCUSD func (p Pair) IsCryptoFiatPair() bool { - return (storage.IsCryptocurrency(p.Base) && storage.IsFiatCurrency(p.Quote)) || - (storage.IsFiatCurrency(p.Base) && storage.IsCryptocurrency(p.Quote)) + return (p.Base.IsCryptocurrency() && p.Quote.IsFiatCurrency()) || + (p.Base.IsFiatCurrency() && p.Quote.IsCryptocurrency()) } // IsFiatPair checks to see if the pair is a fiat pair e.g. EURUSD func (p Pair) IsFiatPair() bool { - return storage.IsFiatCurrency(p.Base) && storage.IsFiatCurrency(p.Quote) + return p.Base.IsFiatCurrency() && p.Quote.IsFiatCurrency() +} + +// IsCryptoStablePair checks to see if the pair is a crypto stable pair e.g. +// LTC-USDT +func (p Pair) IsCryptoStablePair() bool { + return (p.Base.IsCryptocurrency() && p.Quote.IsStableCurrency()) || + (p.Base.IsStableCurrency() && p.Quote.IsCryptocurrency()) +} + +// IsStablePair checks to see if the pair is a stable pair e.g. USDT-DAI +func (p Pair) IsStablePair() bool { + return p.Base.IsStableCurrency() && p.Quote.IsStableCurrency() } // IsInvalid checks invalid pair if base and quote are the same func (p Pair) IsInvalid() bool { - return p.Base.Item == p.Quote.Item + return p.Base.Equal(p.Quote) } // Swap turns the currency pair into its reciprocal @@ -111,7 +111,23 @@ func (p Pair) IsEmpty() bool { return p.Base.IsEmpty() && p.Quote.IsEmpty() } -// ContainsCurrency checks to see if a pair contains a specific currency -func (p Pair) ContainsCurrency(c Code) bool { - return p.Base.Item == c.Item || p.Quote.Item == c.Item +// Contains checks to see if a pair contains a specific currency +func (p Pair) Contains(c Code) bool { + return p.Base.Equal(c) || p.Quote.Equal(c) +} + +// Len derives full length for match exclusion. +func (p Pair) Len() int { + return len(p.Base.String()) + len(p.Quote.String()) +} + +// Other returns the other currency from pair, if not matched returns empty code. +func (p Pair) Other(c Code) (Code, error) { + if p.Base.Equal(c) { + return p.Quote, nil + } + if p.Quote.Equal(c) { + return p.Base, nil + } + return EMPTYCODE, ErrCurrencyCodeEmpty } diff --git a/currency/pair_test.go b/currency/pair_test.go index 09d66c474fc..771cda7f799 100644 --- a/currency/pair_test.go +++ b/currency/pair_test.go @@ -2,6 +2,7 @@ package currency import ( "encoding/json" + "errors" "testing" ) @@ -120,6 +121,34 @@ func TestIsFiatPair(t *testing.T) { } } +func TestIsCryptoStablePair(t *testing.T) { + if !NewPair(BTC, USDT).IsCryptoStablePair() { + t.Error("TestIsCryptoStablePair. Expected true result") + } + + if !NewPair(DAI, USDT).IsCryptoStablePair() { + t.Error("TestIsCryptoStablePair. Expected true result") + } + + if NewPair(AUD, USDT).IsCryptoStablePair() { + t.Error("TestIsCryptoStablePair. Expected false result") + } +} + +func TestIsStablePair(t *testing.T) { + if !NewPair(USDT, DAI).IsStablePair() { + t.Error("TestIsStablePair. Expected true result") + } + + if NewPair(USDT, AUD).IsStablePair() { + t.Error("TestIsStablePair. Expected false result") + } + + if NewPair(USDT, LTC).IsStablePair() { + t.Error("TestIsStablePair. Expected false result") + } +} + func TestString(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) @@ -132,7 +161,7 @@ func TestString(t *testing.T) { func TestFirstCurrency(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - if actual, expected := pair.Base, BTC; actual != expected { + if actual, expected := pair.Base, BTC; !actual.Equal(expected) { t.Errorf( "GetFirstCurrency(): %s was not equal to expected value: %s", actual, expected, @@ -143,7 +172,7 @@ func TestFirstCurrency(t *testing.T) { func TestSecondCurrency(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - if actual, expected := pair.Quote, USD; actual != expected { + if actual, expected := pair.Quote, USD; !actual.Equal(expected) { t.Errorf( "GetSecondCurrency(): %s was not equal to expected value: %s", actual, expected, @@ -513,26 +542,22 @@ func TestNewPairFromFormattedPairs(t *testing.T) { func TestContainsCurrency(t *testing.T) { p := NewPair(BTC, USD) - if !p.ContainsCurrency(BTC) { - t.Error("TestContainsCurrency: Expected currency was not found") + if !p.Contains(BTC) { + t.Error("TestContains: Expected currency was not found") } - if p.ContainsCurrency(ETH) { - t.Error("TestContainsCurrency: Non-existent currency was found") + if p.Contains(ETH) { + t.Error("TestContains: Non-existent currency was found") } } func TestFormatPairs(t *testing.T) { - newP, err := FormatPairs([]string{""}, "-", "") - if err != nil { - t.Error("FormatPairs() error", err) + _, err := FormatPairs([]string{""}, "-", "") + if !errors.Is(err, errEmptyPairString) { + t.Fatalf("received: '%v' but expected: '%v'", err, errEmptyPairString) } - if len(newP) > 0 { - t.Error("TestFormatPairs: Empty string returned a valid pair") - } - - newP, err = FormatPairs([]string{defaultPairWDelimiter}, "-", "") + newP, err := FormatPairs([]string{defaultPairWDelimiter}, "-", "") if err != nil { t.Error("FormatPairs() error", err) } @@ -599,8 +624,8 @@ func TestFindPairDifferences(t *testing.T) { } emptyPairsList, err := NewPairsFromStrings([]string{""}) - if err != nil { - t.Fatal(err) + if !errors.Is(err, errCannotCreatePair) { + t.Fatalf("received: '%v' but expected: '%v'", err, errCannotCreatePair) } // Test that we don't allow empty strings for new pairs @@ -778,7 +803,7 @@ func TestPairFormat_Format(t *testing.T) { { name: "empty", fields: fields{}, - arg: Pair{}, + arg: EMPTYPAIR, want: "", }, { @@ -820,3 +845,23 @@ func TestPairFormat_Format(t *testing.T) { }) } } + +func TestOther(t *testing.T) { + received, err := NewPair(DAI, XRP).Other(DAI) + if err != nil { + t.Fatal(err) + } + if !received.Equal(XRP) { + t.Fatal("unexpected value") + } + received, err = NewPair(DAI, XRP).Other(XRP) + if err != nil { + t.Fatal(err) + } + if !received.Equal(DAI) { + t.Fatal("unexpected value") + } + if _, err := NewPair(DAI, XRP).Other(BTC); !errors.Is(err, ErrCurrencyCodeEmpty) { + t.Fatal("unexpected value") + } +} diff --git a/currency/pairs.go b/currency/pairs.go index e7ab204c912..f415dbdb5ec 100644 --- a/currency/pairs.go +++ b/currency/pairs.go @@ -2,36 +2,48 @@ package currency import ( "encoding/json" + "errors" + "fmt" "math/rand" "strings" "github.com/thrasher-corp/gocryptotrader/log" ) +var ( + errSymbolEmpty = errors.New("symbol is empty") + errPairsEmpty = errors.New("pairs are empty") + errNoDelimiter = errors.New("no delimiter was supplied") +) + // NewPairsFromStrings takes in currency pair strings and returns a currency // pair list func NewPairsFromStrings(pairs []string) (Pairs, error) { - var newPairs Pairs + allThePairs := make(Pairs, len(pairs)) + var err error for i := range pairs { - if pairs[i] == "" { - continue - } - - newPair, err := NewPairFromString(pairs[i]) + allThePairs[i], err = NewPairFromString(pairs[i]) if err != nil { return nil, err } + } + return allThePairs, nil +} - newPairs = append(newPairs, newPair) +// NewPairsFromString takes in a delimiter string and returns a Pairs +// type +func NewPairsFromString(pairs, delimiter string) (Pairs, error) { + if delimiter == "" { + return nil, errNoDelimiter } - return newPairs, nil + return NewPairsFromStrings(strings.Split(pairs, delimiter)) } // Strings returns a slice of strings referring to each currency pair func (p Pairs) Strings() []string { - var list []string + list := make([]string, len(p)) for i := range p { - list = append(list, p[i].String()) + list[i] = p[i].String() } return list } @@ -43,28 +55,22 @@ func (p Pairs) Join() string { // Format formats the pair list to the exchange format configuration func (p Pairs) Format(delimiter, index string, uppercase bool) Pairs { - var pairs Pairs - for i := range p { - var formattedPair = Pair{ - Delimiter: delimiter, - Base: p[i].Base, - Quote: p[i].Quote, - } + pairs := make(Pairs, 0, len(p)) + var err error + for _, format := range p { if index != "" { - newP, err := NewPairFromIndex(p[i].String(), index) + format, err = NewPairFromIndex(format.String(), index) if err != nil { log.Errorf(log.Global, "failed to create NewPairFromIndex. Err: %s\n", err) continue } - formattedPair.Base = newP.Base - formattedPair.Quote = newP.Quote } - + format.Delimiter = delimiter if uppercase { - pairs = append(pairs, formattedPair.Upper()) + pairs = append(pairs, format.Upper()) } else { - pairs = append(pairs, formattedPair.Lower()) + pairs = append(pairs, format.Lower()) } } return pairs @@ -83,18 +89,8 @@ func (p *Pairs) UnmarshalJSON(d []byte) error { return nil } - var allThePairs Pairs - oldPairs := strings.Split(pairs, ",") - for i := range oldPairs { - pair, err := NewPairFromString(oldPairs[i]) - if err != nil { - return err - } - allThePairs = append(allThePairs, pair) - } - - *p = allThePairs - return nil + *p, err = NewPairsFromString(pairs, ",") + return err } // MarshalJSON conforms type to the marshaler interface @@ -102,13 +98,22 @@ func (p Pairs) MarshalJSON() ([]byte, error) { return json.Marshal(p.Join()) } -// Upper returns an upper formatted pair list +// Upper updates the original pairs and returns the pairs for convenience if +// needed. func (p Pairs) Upper() Pairs { - var upper Pairs for i := range p { - upper = append(upper, p[i].Upper()) + p[i] = p[i].Upper() + } + return p +} + +// Lower updates the original pairs and returns the pairs for convenience if +// needed. +func (p Pairs) Lower() Pairs { + for i := range p { + p[i] = p[i].Lower() } - return upper + return p } // Contains checks to see if a specified pair exists inside a currency pair @@ -131,9 +136,22 @@ func (p Pairs) Contains(check Pair, exact bool) bool { // RemovePairsByFilter checks to see if a pair contains a specific currency // and removes it from the list of pairs func (p Pairs) RemovePairsByFilter(filter Code) Pairs { - var pairs Pairs + pairs := make(Pairs, 0, len(p)) + for i := range p { + if p[i].Contains(filter) { + continue + } + pairs = append(pairs, p[i]) + } + return pairs +} + +// GetPairsByFilter returns all pairs that have at least one match base or quote +// to the filter code. +func (p Pairs) GetPairsByFilter(filter Code) Pairs { + pairs := make(Pairs, 0, len(p)) for i := range p { - if p[i].ContainsCurrency(filter) { + if !p[i].Contains(filter) { continue } pairs = append(pairs, p[i]) @@ -143,7 +161,7 @@ func (p Pairs) RemovePairsByFilter(filter Code) Pairs { // Remove removes the specified pair from the list of pairs if it exists func (p Pairs) Remove(pair Pair) Pairs { - var pairs Pairs + pairs := make(Pairs, 0, len(p)) for x := range p { if p[x].Equal(pair) { continue @@ -162,6 +180,18 @@ func (p Pairs) Add(pair Pair) Pairs { return p } +// GetMatch returns either the pair that is equal including the reciprocal for +// when currencies are constructed from different exchange pairs e.g. Exchange +// one USDT-DAI to exchange two DAI-USDT enabled/available pairs. +func (p Pairs) GetMatch(pair Pair) (Pair, error) { + for x := range p { + if p[x].EqualIncludeReciprocal(pair) { + return p[x], nil + } + } + return EMPTYPAIR, ErrPairNotFound +} + // FindDifferences returns pairs which are new or have been removed func (p Pairs) FindDifferences(pairs Pairs) (newPairs, removedPairs Pairs) { for x := range pairs { @@ -188,5 +218,116 @@ func (p Pairs) GetRandomPair() Pair { if pairsLen := len(p); pairsLen != 0 { return p[rand.Intn(pairsLen)] // nolint:gosec // basic number generation required, no need for crypo/rand } - return Pair{} + return EMPTYPAIR +} + +// DeriveFrom matches symbol string to the available pairs list when no +// delimiter is supplied. +func (p Pairs) DeriveFrom(symbol string) (Pair, error) { + if len(p) == 0 { + return EMPTYPAIR, errPairsEmpty + } + if symbol == "" { + return EMPTYPAIR, errSymbolEmpty + } + symbol = strings.ToLower(symbol) +pairs: + for x := range p { + if p[x].Len() != len(symbol) { + continue + } + base := p[x].Base.Lower().String() + baseLength := len(base) + for y := 0; y < baseLength; y++ { + if base[y] != symbol[y] { + continue pairs + } + } + quote := p[x].Quote.Lower().String() + for y := 0; y < len(quote); y++ { + if quote[y] != symbol[baseLength+y] { + continue pairs + } + } + return p[x], nil + } + return EMPTYPAIR, fmt.Errorf("%w for symbol string %s", ErrPairNotFound, symbol) +} + +// GetCrypto returns all the cryptos contained in the list. +func (p Pairs) GetCrypto() Currencies { + m := make(map[*Item]bool) + for x := range p { + if p[x].Base.IsCryptocurrency() { + m[p[x].Base.Item] = p[x].Base.UpperCase + } + if p[x].Quote.IsCryptocurrency() { + m[p[x].Quote.Item] = p[x].Quote.UpperCase + } + } + return currencyConstructor(m) +} + +// GetFiat returns all the cryptos contained in the list. +func (p Pairs) GetFiat() Currencies { + m := make(map[*Item]bool) + for x := range p { + if p[x].Base.IsFiatCurrency() { + m[p[x].Base.Item] = p[x].Base.UpperCase + } + if p[x].Quote.IsFiatCurrency() { + m[p[x].Quote.Item] = p[x].Quote.UpperCase + } + } + return currencyConstructor(m) +} + +// GetCurrencies returns the full currency code list contained derived from the +// pairs list. +func (p Pairs) GetCurrencies() Currencies { + m := make(map[*Item]bool) + for x := range p { + m[p[x].Base.Item] = p[x].Base.UpperCase + m[p[x].Quote.Item] = p[x].Quote.UpperCase + } + return currencyConstructor(m) +} + +// GetStables returns the stable currency code list derived from the pairs list. +func (p Pairs) GetStables() Currencies { + m := make(map[*Item]bool) + for x := range p { + if p[x].Base.IsStableCurrency() { + m[p[x].Base.Item] = p[x].Base.UpperCase + } + if p[x].Quote.IsStableCurrency() { + m[p[x].Quote.Item] = p[x].Quote.UpperCase + } + } + return currencyConstructor(m) +} + +// currencyConstructor takes in an item map and returns the currencies with +// the same formatting. +func currencyConstructor(m map[*Item]bool) Currencies { + var cryptos = make([]Code, len(m)) + var target int + for code, upper := range m { + cryptos[target].Item = code + cryptos[target].UpperCase = upper + target++ + } + return cryptos +} + +// GetStablesMatch returns all stable pairs matched with code +func (p Pairs) GetStablesMatch(code Code) Pairs { + stablePairs := make([]Pair, 0, len(p)) + for x := range p { + if p[x].Base.IsStableCurrency() && p[x].Quote.Equal(code) || + p[x].Quote.IsStableCurrency() && p[x].Base.Equal(code) { + stablePairs = append(stablePairs, p[x]) + } + } + return stablePairs } diff --git a/currency/pairs_test.go b/currency/pairs_test.go index 87429817bc1..aedd8721725 100644 --- a/currency/pairs_test.go +++ b/currency/pairs_test.go @@ -2,6 +2,7 @@ package currency import ( "encoding/json" + "errors" "testing" ) @@ -10,9 +11,18 @@ func TestPairsUpper(t *testing.T) { if err != nil { t.Fatal(err) } - expected := "BTC_USD,BTC_AUD,BTC_LTC" + if expected := "BTC_USD,BTC_AUD,BTC_LTC"; pairs.Upper().Join() != expected { + t.Errorf("Pairs Join() error expected %s but received %s", + expected, pairs.Join()) + } +} - if pairs.Upper().Join() != expected { +func TestPairsLower(t *testing.T) { + pairs, err := NewPairsFromStrings([]string{"BTC_USD", "BTC_AUD", "BTC_LTC"}) + if err != nil { + t.Fatal(err) + } + if expected := "btc_usd,btc_aud,btc_ltc"; pairs.Lower().Join() != expected { t.Errorf("Pairs Join() error expected %s but received %s", expected, pairs.Join()) } @@ -33,6 +43,33 @@ func TestPairsString(t *testing.T) { } } +func TestPairsFromString(t *testing.T) { + if _, err := NewPairsFromString("", ""); !errors.Is(err, errNoDelimiter) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoDelimiter) + } + + if _, err := NewPairsFromString("", ","); !errors.Is(err, errCannotCreatePair) { + t.Fatalf("received: '%v' but expected: '%v'", err, errCannotCreatePair) + } + + pairs, err := NewPairsFromString("ALGO-AUD,BAT-AUD,BCH-AUD,BSV-AUD,BTC-AUD,COMP-AUD,ENJ-AUD,ETC-AUD,ETH-AUD,ETH-BTC,GNT-AUD,LINK-AUD,LTC-AUD,LTC-BTC,MCAU-AUD,OMG-AUD,POWR-AUD,UNI-AUD,USDT-AUD,XLM-AUD,XRP-AUD,XRP-BTC", ",") + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + expected := []string{"ALGO-AUD", "BAT-AUD", "BCH-AUD", "BSV-AUD", "BTC-AUD", + "COMP-AUD", "ENJ-AUD", "ETC-AUD", "ETH-AUD", "ETH-BTC", "GNT-AUD", + "LINK-AUD", "LTC-AUD", "LTC-BTC", "MCAU-AUD", "OMG-AUD", "POWR-AUD", + "UNI-AUD", "USDT-AUD", "XLM-AUD", "XRP-AUD", "XRP-BTC"} + + returned := pairs.Strings() + for x := range returned { + if returned[x] != expected[x] { + t.Fatalf("received: '%v' but expected: '%v'", returned[x], expected[x]) + } + } +} + func TestPairsJoin(t *testing.T) { pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) if err != nil { @@ -157,6 +194,20 @@ func TestRemovePairsByFilter(t *testing.T) { } } +func TestGetPairsByFilter(t *testing.T) { + var pairs = Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(LTC, USDT), + } + + filtered := pairs.GetPairsByFilter(LTC) + if !filtered.Contains(NewPair(LTC, USDT), true) && + !filtered.Contains(NewPair(LTC, USD), true) { + t.Error("TestRemovePairsByFilter unexpected result") + } +} + func TestRemove(t *testing.T) { var pairs = Pairs{ NewPair(BTC, USD), @@ -215,3 +266,242 @@ func TestContains(t *testing.T) { t.Errorf("TestContains: Non-existent pair was found") } } + +func TestDeriveFrom(t *testing.T) { + t.Parallel() + _, err := Pairs{}.DeriveFrom("") + if !errors.Is(err, errPairsEmpty) { + t.Fatalf("received: '%v' but expected: '%v'", err, errPairsEmpty) + } + var testCases = Pairs{ + NewPair(BTC, USDT), + NewPair(USDC, USDT), + NewPair(USDC, USD), + NewPair(BTC, LTC), + NewPair(LTC, SAFEMARS), + } + + _, err = testCases.DeriveFrom("") + if !errors.Is(err, errSymbolEmpty) { + t.Fatalf("received: '%v' but expected: '%v'", err, errSymbolEmpty) + } + + _, err = testCases.DeriveFrom("btcUSD") + if !errors.Is(err, ErrPairNotFound) { + t.Fatalf("received: '%v' but expected: '%v'", err, ErrPairNotFound) + } + + got, err := testCases.DeriveFrom("USDCUSD") + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if got.Upper().String() != "USDCUSD" { + t.Fatalf("received: '%v' but expected: '%v'", got.Upper().String(), "USDCUSD") + } +} + +func TestGetCrypto(t *testing.T) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + } + contains(t, []Code{BTC, LTC, USDT}, pairs.GetCrypto()) +} + +func TestGetFiat(t *testing.T) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + } + contains(t, []Code{USD, NZD}, pairs.GetFiat()) +} + +func TestGetCurrencies(t *testing.T) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + } + contains(t, []Code{BTC, USD, LTC, NZD, USDT}, pairs.GetCurrencies()) +} + +func TestGetStables(t *testing.T) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(DAI, USDT), + NewPair(LTC, USDC), + NewPair(USDP, USDT), + } + contains(t, []Code{USDT, USDP, USDC, DAI}, pairs.GetStables()) +} + +func contains(t *testing.T, c1, c2 []Code) { + t.Helper() +codes: + for x := range c1 { + for y := range c2 { + if c1[x].Equal(c2[y]) { + continue codes + } + } + t.Fatalf("cannot find currency %s in returned currency list %v", c1[x], c2) + } +} + +// Current: 6176922 260.0 ns/op 48 B/op 1 allocs/op +// Prior: 2575473 474.2 ns/op 112 B/op 3 allocs/op +func BenchmarkGetCrypto(b *testing.B) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + } + + for x := 0; x < b.N; x++ { + _ = pairs.GetCrypto() + } +} + +func TestGetMatch(t *testing.T) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + } + + _, err := pairs.GetMatch(NewPair(BTC, WABI)) + if !errors.Is(err, ErrPairNotFound) { + t.Fatalf("received: '%v' but expected '%v'", err, ErrPairNotFound) + } + + expected := NewPair(BTC, USD) + match, err := pairs.GetMatch(expected) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected '%v'", err, nil) + } + + if !match.Equal(expected) { + t.Fatalf("received: '%v' but expected '%v'", match, expected) + } + + match, err = pairs.GetMatch(NewPair(USD, BTC)) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected '%v'", err, nil) + } + if !match.Equal(expected) { + t.Fatalf("received: '%v' but expected '%v'", match, expected) + } +} + +func TestGetStablesMatch(t *testing.T) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(LTC, DAI), + NewPair(USDT, XRP), + NewPair(DAI, XRP), + } + + stablePairs := pairs.GetStablesMatch(BTC) + if len(stablePairs) != 0 { + t.Fatal("unexpected value") + } + + stablePairs = pairs.GetStablesMatch(USD) + if len(stablePairs) != 0 { + t.Fatal("unexpected value") + } + + stablePairs = pairs.GetStablesMatch(LTC) + if len(stablePairs) != 2 { + t.Fatal("unexpected value") + } + + if !stablePairs[0].Equal(NewPair(LTC, USDT)) { + t.Fatal("unexpected value") + } + + if !stablePairs[1].Equal(NewPair(LTC, DAI)) { + t.Fatal("unexpected value") + } + + stablePairs = pairs.GetStablesMatch(XRP) + if len(stablePairs) != 2 { + t.Fatal("unexpected value") + } + + if !stablePairs[0].Equal(NewPair(USDT, XRP)) { + t.Fatal("unexpected value") + } + + if !stablePairs[1].Equal(NewPair(DAI, XRP)) { + t.Fatal("unexpected value") + } +} + +// Current: 5594431 217.4 ns/op 168 B/op 8 allocs/op +// Prev: 3490366 373.4 ns/op 296 B/op 11 allocs/op +func BenchmarkPairsString(b *testing.B) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(LTC, DAI), + NewPair(USDT, XRP), + NewPair(DAI, XRP), + } + + for x := 0; x < b.N; x++ { + _ = pairs.Strings() + } +} + +// Current: 6691011 184.6 ns/op 352 B/op 1 allocs/op +// Prev: 3746151 317.1 ns/op 720 B/op 4 allocs/op +func BenchmarkPairsFormat(b *testing.B) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(LTC, DAI), + NewPair(USDT, XRP), + NewPair(DAI, XRP), + } + + for x := 0; x < b.N; x++ { + _ = pairs.Format("/", "", false) + } +} + +// current: 13075897 100.4 ns/op 352 B/op 1 allocs/o +// prev: 8188616 148.0 ns/op 336 B/op 3 allocs/op +func BenchmarkRemovePairsByFilter(b *testing.B) { + pairs := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(LTC, DAI), + NewPair(USDT, XRP), + NewPair(DAI, XRP), + } + + for x := 0; x < b.N; x++ { + _ = pairs.RemovePairsByFilter(USD) + } +} diff --git a/currency/storage.go b/currency/storage.go index 8f6884973b6..13ee017eb73 100644 --- a/currency/storage.go +++ b/currency/storage.go @@ -15,10 +15,38 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) +// storage is an overarching type that keeps track of and updates currency, +// currency exchange rates and pairs +var storage Storage + func init() { storage.SetDefaults() } +// CurrencyFileUpdateDelay defines the rate at which the currency.json file is +// updated +const ( + DefaultCurrencyFileDelay = 168 * time.Hour + DefaultForeignExchangeDelay = 1 * time.Minute + DefaultStorageFile = "currency.json" + DefaultForexProviderExchangeRatesAPI = "ExchangeRateHost" +) + +var ( + // ErrFiatDisplayCurrencyIsNotFiat defines an error for when the fiat + // display currency is not set as a fiat currency. + ErrFiatDisplayCurrencyIsNotFiat = errors.New("fiat display currency is not a fiat currency") + + errUnexpectedRole = errors.New("unexpected currency role") + errFiatDisplayCurrencyUnset = errors.New("fiat display currency is unset") + errNoFilePathSet = errors.New("no file path set") + errInvalidCurrencyFileUpdateDuration = errors.New("invalid currency file update duration") + errInvalidForeignExchangeUpdateDuration = errors.New("invalid foreign exchange update duration") + errNoForeignExchangeProvidersEnabled = errors.New("no foreign exchange providers enabled") + errNotFiatCurrency = errors.New("not a fiat currency") + errInvalidAmount = errors.New("invalid amount") +) + // SetDefaults sets storage defaults for basic package functionality func (s *Storage) SetDefaults() { s.defaultBaseCurrency = USD @@ -31,14 +59,19 @@ func (s *Storage) SetDefaults() { fiatCurrencies = append(fiatCurrencies, Code{Item: item, UpperCase: true}) } - err := s.SetDefaultFiatCurrencies(fiatCurrencies...) + err := s.SetDefaultFiatCurrencies(fiatCurrencies) + if err != nil { + log.Errorf(log.Currency, "Currency Storage: Setting default fiat currencies error: %s", err) + } + + err = s.SetStableCoins(stables) if err != nil { - log.Errorf(log.Global, "Currency Storage: Setting default fiat currencies error: %s", err) + log.Errorf(log.Currency, "Currency Storage: Setting default stable currencies error: %s", err) } - err = s.SetDefaultCryptocurrencies(BTC, LTC, ETH, DOGE, DASH, XRP, XMR, USDT, UST) + err = s.SetDefaultCryptocurrencies(Currencies{BTC, LTC, ETH, DOGE, DASH, XRP, XMR, USDT, UST}) if err != nil { - log.Errorf(log.Global, "Currency Storage: Setting default cryptocurrencies error: %s", err) + log.Errorf(log.Currency, "Currency Storage: Setting default cryptocurrencies error: %s", err) } s.SetupConversionRates() s.fiatExchangeMarkets = forexprovider.NewDefaultFXProvider() @@ -48,142 +81,139 @@ func (s *Storage) SetDefaults() { // dump file and keep foreign exchange rates updated as fast as possible without // triggering rate limiters, it will also run a full cryptocurrency check // through coin market cap and expose analytics for exchange services -func (s *Storage) RunUpdater(overrides BotOverrides, settings *MainConfiguration, filePath string) error { - s.mtx.Lock() - s.shutdown = make(chan struct{}) - - if !settings.Cryptocurrencies.HasData() { - s.mtx.Unlock() - return errors.New("currency storage error, no cryptocurrencies loaded") - } - s.cryptocurrencies = settings.Cryptocurrencies - +func (s *Storage) RunUpdater(overrides BotOverrides, settings *Config, filePath string) error { if settings.FiatDisplayCurrency.IsEmpty() { - s.mtx.Unlock() - return errors.New("currency storage error, no fiat display currency set in config") + return errFiatDisplayCurrencyUnset } - s.baseCurrency = settings.FiatDisplayCurrency - log.Debugf(log.Global, - "Fiat display currency: %s.\n", - s.baseCurrency) - - if settings.CryptocurrencyProvider.Enabled { - log.Debugln(log.Global, - "Setting up currency analysis system with Coinmarketcap...") - c := &coinmarketcap.Coinmarketcap{} - c.SetDefaults() - err := c.Setup(coinmarketcap.Settings{ - Name: settings.CryptocurrencyProvider.Name, - Enabled: settings.CryptocurrencyProvider.Enabled, - AccountPlan: settings.CryptocurrencyProvider.AccountPlan, - APIkey: settings.CryptocurrencyProvider.APIkey, - Verbose: settings.CryptocurrencyProvider.Verbose, - }) - if err != nil { - log.Errorf(log.Global, - "Unable to setup CoinMarketCap analysis. Error: %s", err) - settings.CryptocurrencyProvider.Enabled = false - } else { - s.currencyAnalysis = c - } + + if !settings.FiatDisplayCurrency.IsFiatCurrency() { + return fmt.Errorf("%s: %w", settings.FiatDisplayCurrency, ErrFiatDisplayCurrencyIsNotFiat) } if filePath == "" { - s.mtx.Unlock() - return errors.New("currency package runUpdater error filepath not set") + return errNoFilePathSet } - s.path = filepath.Join(filePath, DefaultStorageFile) + if settings.CurrencyFileUpdateDuration <= 0 { + return errInvalidCurrencyFileUpdateDuration + } - if settings.CurrencyDelay.Nanoseconds() == 0 { - s.currencyFileUpdateDelay = DefaultCurrencyFileDelay - } else { - s.currencyFileUpdateDelay = settings.CurrencyDelay + if settings.ForeignExchangeUpdateDuration <= 0 { + return errInvalidForeignExchangeUpdateDuration } - if settings.FxRateDelay.Nanoseconds() == 0 { - s.foreignExchangeUpdateDelay = DefaultForeignExchangeDelay - } else { - s.foreignExchangeUpdateDelay = settings.FxRateDelay + s.mtx.Lock() + s.shutdown = make(chan struct{}) + s.baseCurrency = settings.FiatDisplayCurrency + s.path = filepath.Join(filePath, DefaultStorageFile) + s.currencyFileUpdateDelay = settings.CurrencyFileUpdateDuration + s.foreignExchangeUpdateDelay = settings.ForeignExchangeUpdateDuration + + log.Debugf(log.Currency, "Fiat display currency: %s.\n", s.baseCurrency) + var err error + if overrides.Coinmarketcap { + if settings.CryptocurrencyProvider.APIKey != "" && + settings.CryptocurrencyProvider.APIKey != "Key" { + log.Debugln(log.Currency, "Setting up currency analysis system with Coinmarketcap...") + s.currencyAnalysis, err = coinmarketcap.NewFromSettings(coinmarketcap.Settings(settings.CryptocurrencyProvider)) + if err != nil { + log.Errorf(log.Currency, "Unable to setup CoinMarketCap analysis. Error: %s", err) + } + } else { + log.Warnf(log.Currency, "%s API key not set, disabling. Please set this in your config.json file\n", + settings.CryptocurrencyProvider.Name) + } } var fxSettings []base.Settings + var primaryProvider bool for i := range settings.ForexProviders { - switch settings.ForexProviders[i].Name { - case "CurrencyConverter": - if overrides.FxCurrencyConverter || - settings.ForexProviders[i].Enabled { - settings.ForexProviders[i].Enabled = true - fxSettings = append(fxSettings, - base.Settings(settings.ForexProviders[i])) - } - - case "CurrencyLayer": - if overrides.FxCurrencyLayer || settings.ForexProviders[i].Enabled { - settings.ForexProviders[i].Enabled = true - fxSettings = append(fxSettings, - base.Settings(settings.ForexProviders[i])) - } - - case "Fixer": - if overrides.FxFixer || settings.ForexProviders[i].Enabled { - settings.ForexProviders[i].Enabled = true - fxSettings = append(fxSettings, - base.Settings(settings.ForexProviders[i])) - } + enabled := (settings.ForexProviders[i].Name == "CurrencyConverter" && overrides.CurrencyConverter) || + (settings.ForexProviders[i].Name == "CurrencyLayer" && overrides.CurrencyLayer) || + (settings.ForexProviders[i].Name == "Fixer" && overrides.Fixer) || + (settings.ForexProviders[i].Name == "OpenExchangeRates" && overrides.OpenExchangeRates) || + (settings.ForexProviders[i].Name == "ExchangeRates" && overrides.ExchangeRates) || + (settings.ForexProviders[i].Name == "ExchangeRateHost" && overrides.ExchangeRateHost) + + if !enabled { + continue + } - case "OpenExchangeRates": - if overrides.FxOpenExchangeRates || - settings.ForexProviders[i].Enabled { - settings.ForexProviders[i].Enabled = true - fxSettings = append(fxSettings, - base.Settings(settings.ForexProviders[i])) + if settings.ForexProviders[i].Name != DefaultForexProviderExchangeRatesAPI { + if settings.ForexProviders[i].APIKey == "" || settings.ForexProviders[i].APIKey == "Key" { + log.Warnf(log.Currency, "%s forex provider API key not set, disabling. Please set this in your config.json file\n", + settings.ForexProviders[i].Name) + settings.ForexProviders[i].Enabled = false + settings.ForexProviders[i].PrimaryProvider = false + continue } - case "ExchangeRates": - // TODO ADD OVERRIDE - if settings.ForexProviders[i].Enabled { - settings.ForexProviders[i].Enabled = true - fxSettings = append(fxSettings, - base.Settings(settings.ForexProviders[i])) + if settings.ForexProviders[i].APIKeyLvl == -1 && settings.ForexProviders[i].Name != "ExchangeRates" { + log.Warnf(log.Currency, "%s APIKey level not set, functionality is limited. Please review this in your config.json file\n", + settings.ForexProviders[i].Name) } + } - case "ExchangeRateHost": - if overrides.FxExchangeRateHost || settings.ForexProviders[i].Enabled { - settings.ForexProviders[i].Enabled = true - fxSettings = append(fxSettings, - base.Settings(settings.ForexProviders[i])) + if settings.ForexProviders[i].PrimaryProvider { + if primaryProvider { + log.Warnf(log.Currency, "%s disabling primary provider, multiple primarys found. Please review providers in your config.json file\n", + settings.ForexProviders[i].Name) + settings.ForexProviders[i].PrimaryProvider = false + } else { + primaryProvider = true } } + fxSettings = append(fxSettings, base.Settings(settings.ForexProviders[i])) } - if len(fxSettings) != 0 { - var err error - s.fiatExchangeMarkets, err = forexprovider.StartFXService(fxSettings) - if err != nil { - s.mtx.Unlock() - return err + if len(fxSettings) == 0 { + log.Warnln(log.Currency, "No foreign exchange providers enabled, setting default provider...") + for x := range settings.ForexProviders { + if settings.ForexProviders[x].Name != DefaultForexProviderExchangeRatesAPI { + continue + } + settings.ForexProviders[x].Enabled = true + settings.ForexProviders[x].PrimaryProvider = true + primaryProvider = true + log.Warnf(log.Currency, "No valid foreign exchange providers configured. Defaulting to %s.", DefaultForexProviderExchangeRatesAPI) + fxSettings = append(fxSettings, base.Settings(settings.ForexProviders[x])) } + } - log.Debugf(log.Global, - "Primary foreign exchange conversion provider %s enabled\n", - s.fiatExchangeMarkets.Primary.Provider.GetName()) + if len(fxSettings) == 0 { + s.mtx.Unlock() + return errNoForeignExchangeProvidersEnabled + } - for i := range s.fiatExchangeMarkets.Support { - log.Debugf(log.Global, - "Support forex conversion provider %s enabled\n", - s.fiatExchangeMarkets.Support[i].Provider.GetName()) + if !primaryProvider { + for x := range settings.ForexProviders { + if settings.ForexProviders[x].Name == fxSettings[0].Name { + settings.ForexProviders[x].PrimaryProvider = true + fxSettings[0].PrimaryProvider = true + log.Warnf(log.Currency, "No primary foreign exchange provider set. Defaulting to %s.", fxSettings[0].Name) + break + } } + } - // Mutex present in this go routine to lock down retrieving rate data - // until this system initially updates - go s.ForeignExchangeUpdater() - } else { - log.Warnln(log.Global, - "No foreign exchange providers enabled in config.json") + s.fiatExchangeMarkets, err = forexprovider.StartFXService(fxSettings) + if err != nil { s.mtx.Unlock() + return err + } + + log.Debugf(log.Currency, "Using primary foreign exchange provider %s\n", + s.fiatExchangeMarkets.Primary.Provider.GetName()) + + for i := range s.fiatExchangeMarkets.Support { + log.Debugf(log.Currency, "Supporting foreign exchange provider %s\n", + s.fiatExchangeMarkets.Support[i].Provider.GetName()) } + // Mutex present in this go routine to lock down retrieving rate data + // until this system initially updates + s.wg.Add(1) + go s.ForeignExchangeUpdater() return nil } @@ -196,7 +226,7 @@ func (s *Storage) SetupConversionRates() { // SetDefaultFiatCurrencies assigns the default fiat currency list and adds it // to the running list -func (s *Storage) SetDefaultFiatCurrencies(c ...Code) error { +func (s *Storage) SetDefaultFiatCurrencies(c Currencies) error { for i := range c { err := s.currencyCodes.UpdateCurrency("", c[i].String(), "", 0, Fiat) if err != nil { @@ -208,9 +238,22 @@ func (s *Storage) SetDefaultFiatCurrencies(c ...Code) error { return nil } +// SetStableCoins assigns the stable currency list and adds it to the running +// list +func (s *Storage) SetStableCoins(c Currencies) error { + for i := range c { + err := s.currencyCodes.UpdateCurrency("", c[i].String(), "", 0, Stable) + if err != nil { + return err + } + } + s.stableCurrencies = append(s.stableCurrencies, c...) + return nil +} + // SetDefaultCryptocurrencies assigns the default cryptocurrency list and adds // it to the running list -func (s *Storage) SetDefaultCryptocurrencies(c ...Code) error { +func (s *Storage) SetDefaultCryptocurrencies(c Currencies) error { for i := range c { err := s.currencyCodes.UpdateCurrency("", c[i].String(), @@ -240,20 +283,17 @@ func (s *Storage) SetupForexProviders(setting ...base.Settings) error { // ForeignExchangeUpdater is a routine that seeds foreign exchange rate and keeps // updated as fast as possible func (s *Storage) ForeignExchangeUpdater() { - log.Debugln(log.Global, - "Foreign exchange updater started, seeding FX rate list..") - - s.wg.Add(1) defer s.wg.Done() + log.Debugln(log.Currency, "Foreign exchange updater started, seeding FX rate list...") err := s.SeedCurrencyAnalysisData() if err != nil { - log.Errorln(log.Global, err) + log.Errorln(log.Currency, err) } err = s.SeedForeignExchangeRates() if err != nil { - log.Errorln(log.Global, err) + log.Errorln(log.Currency, err) } // Unlock main rate retrieval mutex so all routines waiting can get access @@ -270,20 +310,18 @@ func (s *Storage) ForeignExchangeUpdater() { select { case <-s.shutdown: return - case <-SeedForeignExchangeTick.C: go func() { err := s.SeedForeignExchangeRates() if err != nil { - log.Errorln(log.Global, err) + log.Errorln(log.Currency, err) } }() - case <-SeedCurrencyAnalysisTick.C: go func() { err := s.SeedCurrencyAnalysisData() if err != nil { - log.Errorln(log.Global, err) + log.Errorln(log.Currency, err) } }() } @@ -324,7 +362,7 @@ func (s *Storage) SeedCurrencyAnalysisData() error { // loads it into memory func (s *Storage) FetchCurrencyAnalysisData() error { if s.currencyAnalysis == nil { - log.Warnln(log.Global, + log.Warnln(log.Currency, "Currency analysis system offline, please set api keys for coinmarketcap if you wish to use this feature.") return errors.New("currency analysis system offline") } @@ -354,48 +392,55 @@ func (s *Storage) WriteCurrencyDataToFile(path string, mainUpdate bool) error { return file.Write(path, encoded) } +func (s *Storage) checkFileCurrencyData(item *Item, role Role) error { + if item.Role == Unset { + item.Role = role + } + if item.Role != role { + return fmt.Errorf("%w %s expecting: %s", errUnexpectedRole, item.Role, role) + } + return s.currencyCodes.LoadItem(item) +} + // LoadFileCurrencyData loads currencies into the currency codes func (s *Storage) LoadFileCurrencyData(f *File) error { for i := range f.Contracts { - contract := f.Contracts[i] - contract.Role = Contract - err := s.currencyCodes.LoadItem(&contract) + err := s.checkFileCurrencyData(f.Contracts[i], Contract) if err != nil { return err } } for i := range f.Cryptocurrency { - crypto := f.Cryptocurrency[i] - crypto.Role = Cryptocurrency - err := s.currencyCodes.LoadItem(&crypto) + err := s.checkFileCurrencyData(f.Cryptocurrency[i], Cryptocurrency) if err != nil { return err } } for i := range f.Token { - token := f.Token[i] - token.Role = Token - err := s.currencyCodes.LoadItem(&token) + err := s.checkFileCurrencyData(f.Token[i], Token) if err != nil { return err } } for i := range f.FiatCurrency { - fiat := f.FiatCurrency[i] - fiat.Role = Fiat - err := s.currencyCodes.LoadItem(&fiat) + err := s.checkFileCurrencyData(f.FiatCurrency[i], Fiat) if err != nil { return err } } for i := range f.UnsetCurrency { - unset := f.UnsetCurrency[i] - unset.Role = Unset - err := s.currencyCodes.LoadItem(&unset) + err := s.checkFileCurrencyData(f.UnsetCurrency[i], Unset) + if err != nil { + return err + } + } + + for i := range f.Stable { + err := s.checkFileCurrencyData(f.Stable[i], Stable) if err != nil { return err } @@ -526,21 +571,6 @@ func (s *Storage) updateExchangeRates(m map[string]float64) error { return s.fxRates.Update(m) } -// SetupCryptoProvider sets congiguration parameters and starts a new instance -// of the currency analyser -func (s *Storage) SetupCryptoProvider(settings coinmarketcap.Settings) error { - if settings.APIkey == "" || - settings.APIkey == "key" || - settings.AccountPlan == "" || - settings.AccountPlan == "accountPlan" { - return errors.New("currencyprovider error api key or plan not set in config.json") - } - - s.currencyAnalysis = new(coinmarketcap.Coinmarketcap) - s.currencyAnalysis.SetDefaults() - return s.currencyAnalysis.Setup(settings) -} - // GetTotalMarketCryptocurrencies returns the total seeded market // cryptocurrencies func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) { @@ -553,8 +583,8 @@ func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) { // IsDefaultCurrency returns if a currency is a default currency func (s *Storage) IsDefaultCurrency(c Code) bool { for i := range s.defaultFiatCurrencies { - if s.defaultFiatCurrencies[i].Match(c) || - s.defaultFiatCurrencies[i].Match(GetTranslation(c)) { + if s.defaultFiatCurrencies[i].Equal(c) || + s.defaultFiatCurrencies[i].Equal(GetTranslation(c)) { return true } } @@ -565,83 +595,30 @@ func (s *Storage) IsDefaultCurrency(c Code) bool { // cryptocurrency func (s *Storage) IsDefaultCryptocurrency(c Code) bool { for i := range s.defaultCryptoCurrencies { - if s.defaultCryptoCurrencies[i].Match(c) || - s.defaultCryptoCurrencies[i].Match(GetTranslation(c)) { - return true - } - } - return false -} - -// IsFiatCurrency returns if a currency is part of the enabled fiat currency -// list -func (s *Storage) IsFiatCurrency(c Code) bool { - if c.Item.Role != Unset { - return c.Item.Role == Fiat - } - - if c == USDT { - return false - } - - for i := range s.fiatCurrencies { - if s.fiatCurrencies[i].Match(c) || - s.fiatCurrencies[i].Match(GetTranslation(c)) { + if s.defaultCryptoCurrencies[i].Equal(c) || + s.defaultCryptoCurrencies[i].Equal(GetTranslation(c)) { return true } } - - return false -} - -// IsCryptocurrency returns if a cryptocurrency is part of the enabled -// cryptocurrency list -func (s *Storage) IsCryptocurrency(c Code) bool { - if c.Item.Role != Unset { - return c.Item.Role == Cryptocurrency - } - - if c == USD { - return false - } - - for i := range s.cryptocurrencies { - if s.cryptocurrencies[i].Match(c) || - s.cryptocurrencies[i].Match(GetTranslation(c)) { - return true - } - } - return false } // ValidateCode validates string against currency list and returns a currency // code func (s *Storage) ValidateCode(newCode string) Code { - return s.currencyCodes.Register(newCode) + return s.currencyCodes.Register(newCode, Unset) } // ValidateFiatCode validates a fiat currency string and returns a currency // code func (s *Storage) ValidateFiatCode(newCode string) Code { - c := s.currencyCodes.RegisterFiat(newCode) + c := s.currencyCodes.Register(newCode, Fiat) if !s.fiatCurrencies.Contains(c) { s.fiatCurrencies = append(s.fiatCurrencies, c) } return c } -// ValidateCryptoCode validates a cryptocurrency string and returns a currency -// code -// TODO: Update and add in RegisterCrypto member func -func (s *Storage) ValidateCryptoCode(newCode string) Code { - c := s.currencyCodes.Register(newCode) - if !s.cryptocurrencies.Contains(c) { - s.cryptocurrencies = append(s.cryptocurrencies, c) - } - return c -} - // UpdateBaseCurrency changes base currency func (s *Storage) UpdateBaseCurrency(c Code) error { if c.IsFiatCurrency() { @@ -705,9 +682,22 @@ func (s *Storage) UpdateEnabledFiatCurrencies(c Currencies) { // ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen // or vice versa. func (s *Storage) ConvertCurrency(amount float64, from, to Code) (float64, error) { + if amount <= 0 { + return 0, fmt.Errorf("%f %w", amount, errInvalidAmount) + } + if !from.IsFiatCurrency() { + return 0, fmt.Errorf("%s %w", from, errNotFiatCurrency) + } + if !to.IsFiatCurrency() { + return 0, fmt.Errorf("%s %w", to, errNotFiatCurrency) + } + + if from.Equal(to) { // No need to lock down storage for this rate. + return amount, nil + } + s.mtx.Lock() defer s.mtx.Unlock() - if !s.fxRates.HasData() { err := s.SeedDefaultForeignExchangeRates() if err != nil { @@ -762,7 +752,11 @@ func (s *Storage) IsVerbose() bool { func (s *Storage) Shutdown() error { s.mtx.Lock() defer s.mtx.Unlock() + if s.shutdown == nil { + return nil + } close(s.shutdown) s.wg.Wait() + s.shutdown = nil return s.WriteCurrencyDataToFile(s.path, true) } diff --git a/currency/storage_test.go b/currency/storage_test.go index 699f3d7d413..093fd447163 100644 --- a/currency/storage_test.go +++ b/currency/storage_test.go @@ -1,28 +1,249 @@ package currency -import "testing" +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/thrasher-corp/gocryptotrader/database/testhelpers" +) + +func TestMain(m *testing.M) { + var err error + testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp") + if err != nil { + fmt.Printf("failed to create temp file: %v", err) + os.Exit(1) + } + + t := m.Run() + + err = os.RemoveAll(testhelpers.TempDir) + if err != nil { + fmt.Printf("Failed to remove temp db file: %v", err) + } + + os.Exit(t) +} func TestRunUpdater(t *testing.T) { var newStorage Storage - emptyMainConfig := MainConfiguration{} + emptyMainConfig := Config{} err := newStorage.RunUpdater(BotOverrides{}, &emptyMainConfig, "") if err == nil { t.Fatal("storage RunUpdater() error cannot be nil") } - mainConfig := MainConfiguration{ - Cryptocurrencies: NewCurrenciesFromStringArray([]string{"BTC"}), - FiatDisplayCurrency: USD, + mainConfig := Config{} + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "") + if !errors.Is(err, errFiatDisplayCurrencyUnset) { + t.Fatalf("received: '%v' but expected: '%v'", err, errFiatDisplayCurrencyUnset) } + mainConfig.FiatDisplayCurrency = BTC err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "") - if err == nil { - t.Fatal("storage RunUpdater() error cannot be nil") + if !errors.Is(err, ErrFiatDisplayCurrencyIsNotFiat) { + t.Fatalf("received: '%v' but expected: '%v'", err, ErrFiatDisplayCurrencyIsNotFiat) + } + + mainConfig.FiatDisplayCurrency = AUD + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "") + if !errors.Is(err, errNoFilePathSet) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoFilePathSet) + } + + tempDir := testhelpers.TempDir + + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir) + if !errors.Is(err, errInvalidCurrencyFileUpdateDuration) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidCurrencyFileUpdateDuration) + } + + mainConfig.CurrencyFileUpdateDuration = DefaultCurrencyFileDelay + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir) + if !errors.Is(err, errInvalidForeignExchangeUpdateDuration) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidForeignExchangeUpdateDuration) + } + + mainConfig.ForeignExchangeUpdateDuration = DefaultForeignExchangeDelay + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir) + if !errors.Is(err, errNoForeignExchangeProvidersEnabled) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoForeignExchangeProvidersEnabled) + } + + settings := FXSettings{ + Name: "Fixer", + Enabled: true, + APIKey: "wo", + } + + mainConfig.ForexProviders = AllFXSettings{settings} + err = newStorage.RunUpdater(BotOverrides{Fixer: true}, &mainConfig, tempDir) + if errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, "an error") + } + + err = newStorage.Shutdown() + if err != nil { + t.Fatal(err) + } + + settings.Name = "CurrencyConverter" + mainConfig.ForexProviders = AllFXSettings{settings} + err = newStorage.RunUpdater(BotOverrides{CurrencyConverter: true}, &mainConfig, tempDir) + if errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, "an error") + } + + err = newStorage.Shutdown() + if err != nil { + t.Fatal(err) + } + + settings.Name = "CurrencyLayer" + mainConfig.ForexProviders = AllFXSettings{settings} + err = newStorage.RunUpdater(BotOverrides{CurrencyLayer: true}, &mainConfig, tempDir) + if errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, "an error") + } + + err = newStorage.Shutdown() + if err != nil { + t.Fatal(err) + } + + settings.Name = "OpenExchangeRates" + mainConfig.ForexProviders = AllFXSettings{settings} + err = newStorage.RunUpdater(BotOverrides{OpenExchangeRates: true}, &mainConfig, tempDir) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + err = newStorage.Shutdown() + if err != nil { + t.Fatal(err) } - err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "/bla") + settings.Name = "ExchangeRates" + mainConfig.ForexProviders = AllFXSettings{settings} + err = newStorage.RunUpdater(BotOverrides{ExchangeRates: true}, &mainConfig, tempDir) + if errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, "an error") + } + + err = newStorage.Shutdown() + if err != nil { + t.Fatal(err) + } + + settings.Name = "ExchangeRateHost" + mainConfig.ForexProviders = AllFXSettings{settings} + err = newStorage.RunUpdater(BotOverrides{ExchangeRateHost: true}, &mainConfig, tempDir) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + err = newStorage.Shutdown() if err != nil { - t.Fatal("storage RunUpdater() error", err) + t.Fatal(err) + } + + // old config where two providers enabled + oldDefault := FXSettings{ + Name: "ExchangeRates", + Enabled: true, + APIKey: "", // old default provider which did not need api keys. + PrimaryProvider: true, + } + other := FXSettings{ + Name: "OpenExchangeRates", + Enabled: true, + APIKey: "enabled-keys", // Has keys enabled and will fall over to primary + } + defaultProvider := FXSettings{ + // From config this will be included but not necessarily enabled. + Name: "ExchangeRateHost", + Enabled: false, + } + + mainConfig.ForexProviders = AllFXSettings{oldDefault, other, defaultProvider} + err = newStorage.RunUpdater(BotOverrides{ExchangeRates: true, OpenExchangeRates: true}, &mainConfig, tempDir) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if mainConfig.ForexProviders[0].Enabled { + t.Fatal("old default provider should not be enabled due to unset keys") + } + + if mainConfig.ForexProviders[0].PrimaryProvider { + t.Fatal("old default provider should not be a primary provider anymore") + } + + if !mainConfig.ForexProviders[1].Enabled { + t.Fatal("open exchange rates provider with keys set should be enabled") + } + + if !mainConfig.ForexProviders[1].PrimaryProvider { + t.Fatal("open exchange rates provider with keys set should be set as primary provider") + } + + if mainConfig.ForexProviders[2].Enabled { + t.Fatal("actual default provider should not be enabled") + } + + if mainConfig.ForexProviders[2].PrimaryProvider { + t.Fatal("actual default provider should not be designated as primary provider") + } + + err = newStorage.Shutdown() + if err != nil { + t.Fatal(err) + } + + // old config where two providers enabled + oldDefault = FXSettings{ + Name: "ExchangeRates", + Enabled: true, + APIKey: "", // old default provider which did not need api keys. + PrimaryProvider: true, + } + other = FXSettings{ + Name: "OpenExchangeRates", + Enabled: true, + APIKey: "", // Has no keys enabled will fall over to new default provider. + } + + mainConfig.ForexProviders = AllFXSettings{oldDefault, other, defaultProvider} + err = newStorage.RunUpdater(BotOverrides{ExchangeRates: true, OpenExchangeRates: true}, &mainConfig, tempDir) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if mainConfig.ForexProviders[0].Enabled { + t.Fatal("old default provider should not be enabled due to unset keys") + } + + if mainConfig.ForexProviders[0].PrimaryProvider { + t.Fatal("old default provider should not be a primary provider anymore") + } + + if mainConfig.ForexProviders[1].Enabled { + t.Fatal("open exchange rates provider with keys unset should not be enabled") + } + + if mainConfig.ForexProviders[1].PrimaryProvider { + t.Fatal("open exchange rates provider with keys unset should not be set as primary provider") + } + + if !mainConfig.ForexProviders[2].Enabled { + t.Fatal("actual default provider should not be disabled") + } + + if !mainConfig.ForexProviders[2].PrimaryProvider { + t.Fatal("actual default provider should be designated as primary provider") } } diff --git a/currency/storage_types.go b/currency/storage_types.go index 7398ac117fd..3891031dc1c 100644 --- a/currency/storage_types.go +++ b/currency/storage_types.go @@ -8,29 +8,19 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency/forexprovider" ) -// CurrencyFileUpdateDelay defines the rate at which the currency.json file is -// updated -const ( - DefaultCurrencyFileDelay = 168 * time.Hour - DefaultForeignExchangeDelay = 1 * time.Minute - DefaultStorageFile = "currency.json" -) - -// storage is an overarching type that keeps track of and updates currency, -// currency exchange rates and pairs -var storage Storage - // Storage contains the loaded storage currencies supported on available crypto // or fiat marketplaces // NOTE: All internal currencies are upper case type Storage struct { - // FiatCurrencies defines the running fiat currencies in the currency + // fiatCurrencies defines the running fiat currencies in the currency // storage fiatCurrencies Currencies - // Cryptocurrencies defines the running cryptocurrencies in the currency + // cryptocurrencies defines the running cryptocurrencies in the currency // storage cryptocurrencies Currencies - // CurrencyCodes is a full basket of currencies either crypto, fiat, ico or + // stableCurrencies defines the running stable currencies + stableCurrencies Currencies + // currencyCodes is a full basket of currencies either crypto, fiat, ico or // contract being tracked by the currency storage system currencyCodes BaseCodes // Main converting currency diff --git a/currency/symbol_test.go b/currency/symbol_test.go index fb6d0e6715e..a18a7c8fbed 100644 --- a/currency/symbol_test.go +++ b/currency/symbol_test.go @@ -12,7 +12,7 @@ func TestGetSymbolByCurrencyName(t *testing.T) { t.Errorf("TestGetSymbolByCurrencyName differing values") } - _, err = GetSymbolByCurrencyName(Code{}) + _, err = GetSymbolByCurrencyName(EMPTYCODE) if err == nil { t.Errorf("TestGetSymbolByCurrencyNam returned nil on non-existent currency") } diff --git a/currency/translation.go b/currency/translation.go index 5b9075b0f1f..7948ef085ee 100644 --- a/currency/translation.go +++ b/currency/translation.go @@ -3,20 +3,20 @@ package currency // GetTranslation returns similar strings for a particular currency if not found // returns the code back func GetTranslation(currency Code) Code { - val, ok := translations[currency] + val, ok := translations[currency.Item] if !ok { return currency } return val } -var translations = map[Code]Code{ - BTC: XBT, - ETH: XETH, - DOGE: XDG, - USD: USDT, - XBT: BTC, - XETH: ETH, - XDG: DOGE, - USDT: USD, +var translations = map[*Item]Code{ + BTC.Item: XBT, + ETH.Item: XETH, + DOGE.Item: XDG, + USD.Item: USDT, + XBT.Item: BTC, + XETH.Item: ETH, + XDG.Item: DOGE, + USDT.Item: USD, } diff --git a/currency/translation_test.go b/currency/translation_test.go index 95415b3867e..d1254bfdb3f 100644 --- a/currency/translation_test.go +++ b/currency/translation_test.go @@ -1,18 +1,20 @@ package currency -import "testing" +import ( + "testing" +) func TestGetTranslation(t *testing.T) { currencyPair := NewPair(BTC, USD) expected := XBT actual := GetTranslation(currencyPair.Base) - if expected != actual { + if !expected.Equal(actual) { t.Error("GetTranslation: translation result was different to expected result") } currencyPair.Base = NEO actual = GetTranslation(currencyPair.Base) - if actual != currencyPair.Base { + if !actual.Equal(currencyPair.Base) { t.Error("GetTranslation: no error on non translatable currency") } @@ -20,7 +22,15 @@ func TestGetTranslation(t *testing.T) { currencyPair.Base = XBT actual = GetTranslation(currencyPair.Base) - if expected != actual { + if !expected.Equal(actual) { t.Error("GetTranslation: translation result was different to expected result") } + + // This test accentuates the issue of comparing code types as this will + // not match for lower and upper differences and a key (*Item) needs to be + // used. + // Code{Item: 0xc000094140, Upper: true} != Code{Item: 0xc000094140, Upper: false} + if actual = GetTranslation(NewCode("btc")); !XBT.Equal(actual) { + t.Errorf("received: '%v', but expected: '%v'", actual, XBT) + } } diff --git a/engine/currency_state_manager_test.go b/engine/currency_state_manager_test.go index 65dda094140..c0e24099746 100644 --- a/engine/currency_state_manager_test.go +++ b/engine/currency_state_manager_test.go @@ -240,7 +240,7 @@ func TestGetAllRPC(t *testing.T) { func TestCanWithdrawRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.Code{}, "") + _, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.EMPTYCODE, "") if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -248,7 +248,7 @@ func TestCanWithdrawRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanWithdrawRPC("", currency.Code{}, "") + }).CanWithdrawRPC("", currency.EMPTYCODE, "") if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -256,7 +256,7 @@ func TestCanWithdrawRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanWithdrawRPC("", currency.Code{}, "") + }).CanWithdrawRPC("", currency.EMPTYCODE, "") if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -264,7 +264,7 @@ func TestCanWithdrawRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanWithdrawRPC("", currency.Code{}, "") + }).CanWithdrawRPC("", currency.EMPTYCODE, "") if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -272,7 +272,7 @@ func TestCanWithdrawRPC(t *testing.T) { func TestCanDepositRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.Code{}, "") + _, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.EMPTYCODE, "") if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -280,7 +280,7 @@ func TestCanDepositRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanDepositRPC("", currency.Code{}, "") + }).CanDepositRPC("", currency.EMPTYCODE, "") if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -288,7 +288,7 @@ func TestCanDepositRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanDepositRPC("", currency.Code{}, "") + }).CanDepositRPC("", currency.EMPTYCODE, "") if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -296,7 +296,7 @@ func TestCanDepositRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanDepositRPC("", currency.Code{}, "") + }).CanDepositRPC("", currency.EMPTYCODE, "") if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -304,7 +304,7 @@ func TestCanDepositRPC(t *testing.T) { func TestCanTradeRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.Code{}, "") + _, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.EMPTYCODE, "") if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -312,7 +312,7 @@ func TestCanTradeRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanTradeRPC("", currency.Code{}, "") + }).CanTradeRPC("", currency.EMPTYCODE, "") if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -320,7 +320,7 @@ func TestCanTradeRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanTradeRPC("", currency.Code{}, "") + }).CanTradeRPC("", currency.EMPTYCODE, "") if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -328,7 +328,7 @@ func TestCanTradeRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanTradeRPC("", currency.Code{}, "") + }).CanTradeRPC("", currency.EMPTYCODE, "") if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } @@ -336,7 +336,7 @@ func TestCanTradeRPC(t *testing.T) { func TestCanTradePairRPC(t *testing.T) { t.Parallel() - _, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.Pair{}, "") + _, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.EMPTYPAIR, "") if !errors.Is(err, ErrSubSystemNotStarted) { t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted) } @@ -344,7 +344,7 @@ func TestCanTradePairRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true}, - }).CanTradePairRPC("", currency.Pair{}, "") + }).CanTradePairRPC("", currency.EMPTYPAIR, "") if !errors.Is(err, errManager) { t.Fatalf("received: '%v' but expected: '%v'", err, errManager) } @@ -352,7 +352,7 @@ func TestCanTradePairRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true}, - }).CanTradePairRPC("", currency.Pair{}, "") + }).CanTradePairRPC("", currency.EMPTYPAIR, "") if !errors.Is(err, errExchange) { t.Fatalf("received: '%v' but expected: '%v'", err, errExchange) } @@ -360,7 +360,7 @@ func TestCanTradePairRPC(t *testing.T) { _, err = (&CurrencyStateManager{ started: 1, iExchangeManager: &fakeExchangeManagerino{}, - }).CanTradePairRPC("", currency.Pair{}, "") + }).CanTradePairRPC("", currency.EMPTYPAIR, "") if !errors.Is(err, nil) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } diff --git a/engine/engine.go b/engine/engine.go index 9a341274e4e..f857c361c26 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -15,7 +15,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/currency/coinmarketcap" "github.com/thrasher-corp/gocryptotrader/dispatch" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -146,40 +145,44 @@ func loadConfigWithSettings(settings *Settings, flagSet map[string]bool) (*confi return conf, conf.CheckConfig() } +// FlagSet defines set flags from command line args for comparison methods +type FlagSet map[string]bool + +// WithBool checks the supplied flag. If set it will overide the config boolean +// value as a command line takes precedence. If not set will fall back to config +// options. +func (f FlagSet) WithBool(key string, flagValue *bool, configValue bool) { + isSet := f[key] + *flagValue = !isSet && configValue || isSet && *flagValue +} + // validateSettings validates and sets all bot settings -func validateSettings(b *Engine, s *Settings, flagSet map[string]bool) { +func validateSettings(b *Engine, s *Settings, flagSet FlagSet) { b.Settings = *s - b.Settings.EnableDataHistoryManager = (flagSet["datahistorymanager"] && b.Settings.EnableDatabaseManager) || b.Config.DataHistoryManager.Enabled + flagSet.WithBool("coinmarketcap", &b.Settings.EnableCoinmarketcapAnalysis, b.Config.Currency.CryptocurrencyProvider.Enabled) + + flagSet.WithBool("currencyconverter", &b.Settings.EnableCurrencyConverter, b.Config.Currency.ForexProviders.IsEnabled("currencyconverter")) - b.Settings.EnableCurrencyStateManager = (flagSet["currencystatemanager"] && - b.Settings.EnableCurrencyStateManager) || - b.Config.CurrencyStateManager.Enabled != nil && - *b.Config.CurrencyStateManager.Enabled + flagSet.WithBool("currencylayer", &b.Settings.EnableCurrencyLayer, b.Config.Currency.ForexProviders.IsEnabled("currencylayer")) + flagSet.WithBool("exchangerates", &b.Settings.EnableExchangeRates, b.Config.Currency.ForexProviders.IsEnabled("exchangerates")) + flagSet.WithBool("fixer", &b.Settings.EnableFixer, b.Config.Currency.ForexProviders.IsEnabled("fixer")) + flagSet.WithBool("openexchangerates", &b.Settings.EnableOpenExchangeRates, b.Config.Currency.ForexProviders.IsEnabled("openexchangerates")) + flagSet.WithBool("exchangeratehost", &b.Settings.EnableExchangeRateHost, b.Config.Currency.ForexProviders.IsEnabled("exchangeratehost")) - b.Settings.EnableGCTScriptManager = b.Settings.EnableGCTScriptManager && - (flagSet["gctscriptmanager"] || b.Config.GCTScript.Enabled) + flagSet.WithBool("datahistorymanager", &b.Settings.EnableDataHistoryManager, b.Config.DataHistoryManager.Enabled) + flagSet.WithBool("currencystatemanager", &b.Settings.EnableCurrencyStateManager, b.Config.CurrencyStateManager.Enabled != nil && *b.Config.CurrencyStateManager.Enabled) + flagSet.WithBool("gctscriptmanager", &b.Settings.EnableGCTScriptManager, b.Config.GCTScript.Enabled) if b.Settings.EnablePortfolioManager && b.Settings.PortfolioManagerDelay <= 0 { b.Settings.PortfolioManagerDelay = PortfolioSleepDelay } - if !flagSet["grpc"] { - b.Settings.EnableGRPC = b.Config.RemoteControl.GRPC.Enabled - } - - if !flagSet["grpcproxy"] { - b.Settings.EnableGRPCProxy = b.Config.RemoteControl.GRPC.GRPCProxyEnabled - } - - if !flagSet["websocketrpc"] { - b.Settings.EnableWebsocketRPC = b.Config.RemoteControl.WebsocketRPC.Enabled - } - - if !flagSet["deprecatedrpc"] { - b.Settings.EnableDeprecatedRPC = b.Config.RemoteControl.DeprecatedRPC.Enabled - } + flagSet.WithBool("grpc", &b.Settings.EnableGRPC, b.Config.RemoteControl.GRPC.Enabled) + flagSet.WithBool("grpcproxy", &b.Settings.EnableGRPCProxy, b.Config.RemoteControl.GRPC.GRPCProxyEnabled) + flagSet.WithBool("websocketrpc", &b.Settings.EnableWebsocketRPC, b.Config.RemoteControl.WebsocketRPC.Enabled) + flagSet.WithBool("deprecatedrpc", &b.Settings.EnableDeprecatedRPC, b.Config.RemoteControl.DeprecatedRPC.Enabled) if flagSet["maxvirtualmachines"] { maxMachines := uint8(b.Settings.MaxVirtualMachines) @@ -250,7 +253,7 @@ func PrintSettings(s *Settings) { gctlog.Debugf(gctlog.Global, "\t Enable dry run mode: %v", s.EnableDryRun) gctlog.Debugf(gctlog.Global, "\t Enable all exchanges: %v", s.EnableAllExchanges) gctlog.Debugf(gctlog.Global, "\t Enable all pairs: %v", s.EnableAllPairs) - gctlog.Debugf(gctlog.Global, "\t Enable coinmarketcap analysis: %v", s.EnableCoinmarketcapAnalysis) + gctlog.Debugf(gctlog.Global, "\t Enable CoinMarketCap analysis: %v", s.EnableCoinmarketcapAnalysis) gctlog.Debugf(gctlog.Global, "\t Enable portfolio manager: %v", s.EnablePortfolioManager) gctlog.Debugf(gctlog.Global, "\t Enable data history manager: %v", s.EnableDataHistoryManager) gctlog.Debugf(gctlog.Global, "\t Enable currency state manager: %v", s.EnableCurrencyStateManager) @@ -273,16 +276,17 @@ func PrintSettings(s *Settings) { gctlog.Debugf(gctlog.Global, "\t Dispatch package jobs limit: %d", s.DispatchJobsLimit) gctlog.Debugf(gctlog.Global, "- EXCHANGE SYNCER SETTINGS:\n") gctlog.Debugf(gctlog.Global, "\t Exchange sync continuously: %v\n", s.SyncContinuously) - gctlog.Debugf(gctlog.Global, "\t Exchange sync workers: %v\n", s.SyncWorkers) + gctlog.Debugf(gctlog.Global, "\t Exchange sync workers count: %v\n", s.SyncWorkersCount) gctlog.Debugf(gctlog.Global, "\t Enable ticker syncing: %v\n", s.EnableTickerSyncing) gctlog.Debugf(gctlog.Global, "\t Enable orderbook syncing: %v\n", s.EnableOrderbookSyncing) gctlog.Debugf(gctlog.Global, "\t Enable trade syncing: %v\n", s.EnableTradeSyncing) gctlog.Debugf(gctlog.Global, "\t Exchange REST sync timeout: %v\n", s.SyncTimeoutREST) gctlog.Debugf(gctlog.Global, "\t Exchange Websocket sync timeout: %v\n", s.SyncTimeoutWebsocket) gctlog.Debugf(gctlog.Global, "- FOREX SETTINGS:") - gctlog.Debugf(gctlog.Global, "\t Enable currency converter: %v", s.EnableCurrencyConverter) - gctlog.Debugf(gctlog.Global, "\t Enable currency layer: %v", s.EnableCurrencyLayer) - gctlog.Debugf(gctlog.Global, "\t Enable fixer: %v", s.EnableFixer) + gctlog.Debugf(gctlog.Global, "\t Enable Currency Converter: %v", s.EnableCurrencyConverter) + gctlog.Debugf(gctlog.Global, "\t Enable Currency Layer: %v", s.EnableCurrencyLayer) + gctlog.Debugf(gctlog.Global, "\t Enable ExchangeRatesAPI.io: %v", s.EnableExchangeRates) + gctlog.Debugf(gctlog.Global, "\t Enable Fixer: %v", s.EnableFixer) gctlog.Debugf(gctlog.Global, "\t Enable OpenExchangeRates: %v", s.EnableOpenExchangeRates) gctlog.Debugf(gctlog.Global, "\t Enable ExchangeRateHost: %v", s.EnableExchangeRateHost) gctlog.Debugf(gctlog.Global, "- EXCHANGE SETTINGS:") @@ -407,32 +411,20 @@ func (bot *Engine) Start() error { } } } - if bot.Settings.EnableCoinmarketcapAnalysis || - bot.Settings.EnableCurrencyConverter || - bot.Settings.EnableCurrencyLayer || - bot.Settings.EnableFixer || - bot.Settings.EnableOpenExchangeRates || - bot.Settings.EnableExchangeRateHost { - err = currency.RunStorageUpdater(currency.BotOverrides{ - Coinmarketcap: bot.Settings.EnableCoinmarketcapAnalysis, - FxCurrencyConverter: bot.Settings.EnableCurrencyConverter, - FxCurrencyLayer: bot.Settings.EnableCurrencyLayer, - FxFixer: bot.Settings.EnableFixer, - FxOpenExchangeRates: bot.Settings.EnableOpenExchangeRates, - FxExchangeRateHost: bot.Settings.EnableExchangeRateHost, - }, - ¤cy.MainConfiguration{ - ForexProviders: bot.Config.GetForexProviders(), - CryptocurrencyProvider: coinmarketcap.Settings(bot.Config.Currency.CryptocurrencyProvider), - Cryptocurrencies: bot.Config.Currency.Cryptocurrencies, - FiatDisplayCurrency: bot.Config.Currency.FiatDisplayCurrency, - CurrencyDelay: bot.Config.Currency.CurrencyFileUpdateDuration, - FxRateDelay: bot.Config.Currency.ForeignExchangeUpdateDuration, - }, - bot.Settings.DataDir) - if err != nil { - gctlog.Errorf(gctlog.Global, "ExchangeSettings updater system failed to start %s", err) - } + + err = currency.RunStorageUpdater(currency.BotOverrides{ + Coinmarketcap: bot.Settings.EnableCoinmarketcapAnalysis, + CurrencyConverter: bot.Settings.EnableCurrencyConverter, + CurrencyLayer: bot.Settings.EnableCurrencyLayer, + ExchangeRates: bot.Settings.EnableExchangeRates, + Fixer: bot.Settings.EnableFixer, + OpenExchangeRates: bot.Settings.EnableOpenExchangeRates, + ExchangeRateHost: bot.Settings.EnableExchangeRateHost, + }, + &bot.Config.Currency, + bot.Settings.DataDir) + if err != nil { + gctlog.Errorf(gctlog.Global, "ExchangeSettings updater system failed to start %s", err) } if bot.Settings.EnableGRPC { @@ -524,15 +516,17 @@ func (bot *Engine) Start() error { } if bot.Settings.EnableExchangeSyncManager { - exchangeSyncCfg := &Config{ - SyncTicker: bot.Settings.EnableTickerSyncing, - SyncOrderbook: bot.Settings.EnableOrderbookSyncing, - SyncTrades: bot.Settings.EnableTradeSyncing, - SyncContinuously: bot.Settings.SyncContinuously, - NumWorkers: bot.Settings.SyncWorkers, - Verbose: bot.Settings.Verbose, - SyncTimeoutREST: bot.Settings.SyncTimeoutREST, - SyncTimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket, + exchangeSyncCfg := &SyncManagerConfig{ + SynchronizeTicker: bot.Settings.EnableTickerSyncing, + SynchronizeOrderbook: bot.Settings.EnableOrderbookSyncing, + SynchronizeTrades: bot.Settings.EnableTradeSyncing, + SynchronizeContinuously: bot.Settings.SyncContinuously, + TimeoutREST: bot.Settings.SyncTimeoutREST, + TimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket, + NumWorkers: bot.Settings.SyncWorkersCount, + Verbose: bot.Settings.Verbose, + FiatDisplayCurrency: bot.Config.Currency.FiatDisplayCurrency, + PairFormatDisplay: bot.Config.Currency.CurrencyPairFormat, } bot.currencyPairSyncer, err = setupSyncManager( @@ -692,15 +686,8 @@ func (bot *Engine) Stop() { } } - if bot.Settings.EnableCoinmarketcapAnalysis || - bot.Settings.EnableCurrencyConverter || - bot.Settings.EnableCurrencyLayer || - bot.Settings.EnableFixer || - bot.Settings.EnableOpenExchangeRates || - bot.Settings.EnableExchangeRateHost { - if err := currency.ShutdownStorageUpdater(); err != nil { - gctlog.Errorf(gctlog.Global, "ExchangeSettings storage system. Error: %v", err) - } + if err := currency.ShutdownStorageUpdater(); err != nil { + gctlog.Errorf(gctlog.Global, "ExchangeSettings storage system. Error: %v", err) } if !bot.Settings.EnableDryRun { diff --git a/engine/engine_test.go b/engine/engine_test.go index 0f0333f7a99..1e4f0cf96b5 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -93,7 +93,7 @@ func TestStartStopDoesNotCausePanic(t *testing.T) { DataDir: tempDir, }, nil) if err != nil { - t.Error(err) + t.Fatal(err) } botOne.Settings.EnableGRPCProxy = false for i := range botOne.Config.Exchanges { @@ -281,3 +281,41 @@ func TestDryRunParamInteraction(t *testing.T) { t.Error("dryrun should be true and verbose should be true") } } + +func TestFlagSetWith(t *testing.T) { + var isRunning bool + flags := make(FlagSet) + // Flag not set default to config + flags.WithBool("NOT SET", &isRunning, true) + if !isRunning { + t.Fatalf("received: '%v' but expected: '%v'", isRunning, true) + } + flags.WithBool("NOT SET", &isRunning, false) + if isRunning { + t.Fatalf("received: '%v' but expected: '%v'", isRunning, false) + } + + flags["IS SET"] = true + isRunning = true + // Flag set true which will overide config + flags.WithBool("IS SET", &isRunning, true) + if !isRunning { + t.Fatalf("received: '%v' but expected: '%v'", isRunning, true) + } + flags.WithBool("IS SET", &isRunning, false) + if !isRunning { + t.Fatalf("received: '%v' but expected: '%v'", isRunning, true) + } + + flags["IS SET"] = true + isRunning = false + // Flag set false which will overide config + flags.WithBool("IS SET", &isRunning, true) + if isRunning { + t.Fatalf("received: '%v' but expected: '%v'", isRunning, false) + } + flags.WithBool("IS SET", &isRunning, false) + if isRunning { + t.Fatalf("received: '%v' but expected: '%v'", isRunning, false) + } +} diff --git a/engine/engine_types.go b/engine/engine_types.go index f3bc77b6a25..3ecd6a869a0 100644 --- a/engine/engine_types.go +++ b/engine/engine_types.go @@ -44,7 +44,7 @@ type Settings struct { EnableTickerSyncing bool EnableOrderbookSyncing bool EnableTradeSyncing bool - SyncWorkers int + SyncWorkersCount int SyncContinuously bool SyncTimeoutREST time.Duration SyncTimeoutWebsocket time.Duration @@ -52,6 +52,7 @@ type Settings struct { // Forex settings EnableCurrencyConverter bool EnableCurrencyLayer bool + EnableExchangeRates bool EnableFixer bool EnableOpenExchangeRates bool EnableExchangeRateHost bool diff --git a/engine/event_manager_test.go b/engine/event_manager_test.go index e1bcc5ed1b7..fc2ff3d297d 100644 --- a/engine/event_manager_test.go +++ b/engine/event_manager_test.go @@ -27,10 +27,10 @@ func TestSetupEventManager(t *testing.T) { if !errors.Is(err, nil) { t.Fatalf("error '%v', expected '%v'", err, nil) } - if m == nil { + if m == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected manager") } - if m.sleepDelay == 0 { + if m.sleepDelay == 0 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Error("expected default set") } } diff --git a/engine/exchange_manager.go b/engine/exchange_manager.go index 1d0ac43b35d..8b3dbfa8226 100644 --- a/engine/exchange_manager.go +++ b/engine/exchange_manager.go @@ -94,12 +94,16 @@ func (m *ExchangeManager) RemoveExchange(exchName string) error { if m.Len() == 0 { return ErrNoExchangesLoaded } - _, err := m.GetExchangeByName(exchName) + exch, err := m.GetExchangeByName(exchName) if err != nil { return err } m.m.Lock() defer m.m.Unlock() + err = exch.GetBase().Requester.Shutdown() + if err != nil { + return err + } delete(m.exchanges, strings.ToLower(exchName)) log.Infof(log.ExchangeSys, "%s exchange unloaded successfully.\n", exchName) return nil diff --git a/engine/exchange_manager_test.go b/engine/exchange_manager_test.go index 6805691b156..9f717281d57 100644 --- a/engine/exchange_manager_test.go +++ b/engine/exchange_manager_test.go @@ -14,10 +14,10 @@ import ( func TestSetupExchangeManager(t *testing.T) { t.Parallel() m := SetupExchangeManager() - if m == nil { + if m == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatalf("unexpected response") } - if m.exchanges == nil { + if m.exchanges == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Error("unexpected response") } } diff --git a/engine/helpers.go b/engine/helpers.go index 84f0c7a7b83..f8282dba751 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -184,15 +184,16 @@ func (bot *Engine) SetSubsystem(subSystemName string, enable bool) error { case SyncManagerName: if enable { if bot.currencyPairSyncer == nil { - exchangeSyncCfg := &Config{ - SyncTicker: bot.Settings.EnableTickerSyncing, - SyncOrderbook: bot.Settings.EnableOrderbookSyncing, - SyncTrades: bot.Settings.EnableTradeSyncing, - SyncContinuously: bot.Settings.SyncContinuously, - NumWorkers: bot.Settings.SyncWorkers, - Verbose: bot.Settings.Verbose, - SyncTimeoutREST: bot.Settings.SyncTimeoutREST, - SyncTimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket, + exchangeSyncCfg := &SyncManagerConfig{ + SynchronizeTicker: bot.Settings.EnableTickerSyncing, + SynchronizeOrderbook: bot.Settings.EnableOrderbookSyncing, + SynchronizeTrades: bot.Settings.EnableTradeSyncing, + SynchronizeContinuously: bot.Settings.SyncContinuously, + TimeoutREST: bot.Settings.SyncTimeoutREST, + TimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket, + NumWorkers: bot.Settings.SyncWorkersCount, + FiatDisplayCurrency: bot.Config.Currency.FiatDisplayCurrency, + Verbose: bot.Settings.Verbose, } bot.currencyPairSyncer, err = setupSyncManager( exchangeSyncCfg, @@ -376,9 +377,9 @@ func (bot *Engine) GetSpecificAvailablePairs(enabledExchangesOnly, fiatPairs, in for x := range supportedPairs { if fiatPairs { if supportedPairs[x].IsCryptoFiatPair() && - !supportedPairs[x].ContainsCurrency(currency.USDT) || + !supportedPairs[x].Contains(currency.USDT) || (includeUSDT && - supportedPairs[x].ContainsCurrency(currency.USDT) && + supportedPairs[x].Contains(currency.USDT) && supportedPairs[x].IsCryptoPair()) { if pairList.Contains(supportedPairs[x], false) { continue @@ -428,18 +429,12 @@ func (bot *Engine) MapCurrenciesByExchange(p currency.Pairs, enabledExchangesOnl continue } - result, ok := currencyExchange[exchName] - if !ok { - var pairs []currency.Pair - pairs = append(pairs, p[x]) - currencyExchange[exchName] = pairs - } else { - if result.Contains(p[x], false) { - continue - } - result = append(result, p[x]) - currencyExchange[exchName] = result + result := currencyExchange[exchName] + if result.Contains(p[x], false) { + continue } + result = append(result, p[x]) + currencyExchange[exchName] = result } } return currencyExchange @@ -468,18 +463,14 @@ func (bot *Engine) GetExchangeNamesByCurrency(p currency.Pair, enabled bool, ass func GetRelatableCryptocurrencies(p currency.Pair) currency.Pairs { var pairs currency.Pairs cryptocurrencies := currency.GetCryptocurrencies() - for x := range cryptocurrencies { newPair := currency.NewPair(p.Base, cryptocurrencies[x]) if newPair.IsInvalid() { continue } - - if newPair.Base.Upper() == p.Base.Upper() && - newPair.Quote.Upper() == p.Quote.Upper() { + if newPair.Equal(p) { continue } - if pairs.Contains(newPair, false) { continue } @@ -496,12 +487,11 @@ func GetRelatableFiatCurrencies(p currency.Pair) currency.Pairs { for x := range fiatCurrencies { newPair := currency.NewPair(p.Base, fiatCurrencies[x]) - if newPair.Base.Upper() == newPair.Quote.Upper() { + if newPair.Base.Equal(newPair.Quote) { continue } - if newPair.Base.Upper() == p.Base.Upper() && - newPair.Quote.Upper() == p.Quote.Upper() { + if newPair.Equal(p) { continue } @@ -532,17 +522,17 @@ func GetRelatableCurrencies(p currency.Pair, incOrig, incUSDT bool) currency.Pai } first := currency.GetTranslation(p.Base) - if first != p.Base { + if !first.Equal(p.Base) { addPair(currency.NewPair(first, p.Quote)) second := currency.GetTranslation(p.Quote) - if second != p.Quote { + if !second.Equal(p.Quote) { addPair(currency.NewPair(first, second)) } } second := currency.GetTranslation(p.Quote) - if second != p.Quote { + if !second.Equal(p.Quote) { addPair(currency.NewPair(p.Base, second)) } } @@ -586,19 +576,29 @@ func GetCollatedExchangeAccountInfoByCoin(accounts []account.Holdings) map[curre for y := range accounts[x].Accounts { for z := range accounts[x].Accounts[y].Currencies { currencyName := accounts[x].Accounts[y].Currencies[z].CurrencyName - avail := accounts[x].Accounts[y].Currencies[z].TotalValue + total := accounts[x].Accounts[y].Currencies[z].Total onHold := accounts[x].Accounts[y].Currencies[z].Hold + avail := accounts[x].Accounts[y].Currencies[z].AvailableWithoutBorrow + free := accounts[x].Accounts[y].Currencies[z].Free + borrowed := accounts[x].Accounts[y].Currencies[z].Borrowed + info, ok := result[currencyName] if !ok { accountInfo := account.Balance{ - CurrencyName: currencyName, - Hold: onHold, - TotalValue: avail, + CurrencyName: currencyName, + Total: total, + Hold: onHold, + Free: free, + AvailableWithoutBorrow: avail, + Borrowed: borrowed, } result[currencyName] = accountInfo } else { info.Hold += onHold - info.TotalValue += avail + info.Total += total + info.Free += free + info.AvailableWithoutBorrow += avail + info.Borrowed += borrowed result[currencyName] = info } } @@ -641,30 +641,17 @@ func (bot *Engine) GetCryptocurrenciesByExchange(exchangeName string, enabledExc } var err error - var pairs []currency.Pair + var pairs currency.Pairs if enabledPairs { pairs, err = bot.Config.GetEnabledPairs(exchangeName, assetType) - if err != nil { - return nil, err - } } else { pairs, err = bot.Config.GetAvailablePairs(exchangeName, assetType) - if err != nil { - return nil, err - } } - - for y := range pairs { - if pairs[y].Base.IsCryptocurrency() && - !common.StringDataCompareInsensitive(cryptocurrencies, pairs[y].Base.String()) { - cryptocurrencies = append(cryptocurrencies, pairs[y].Base.String()) - } - - if pairs[y].Quote.IsCryptocurrency() && - !common.StringDataCompareInsensitive(cryptocurrencies, pairs[y].Quote.String()) { - cryptocurrencies = append(cryptocurrencies, pairs[y].Quote.String()) - } + if err != nil { + return nil, err } + cryptocurrencies = pairs.GetCrypto().Strings() + break } return cryptocurrencies, nil } @@ -681,7 +668,7 @@ func (bot *Engine) GetCryptocurrencyDepositAddressesByExchange(exchName string) result := bot.GetAllExchangeCryptocurrencyDepositAddresses() r, ok := result[exchName] if !ok { - return nil, ErrExchangeNotFound + return nil, fmt.Errorf("%s %w", exchName, ErrExchangeNotFound) } return r, nil } @@ -689,7 +676,9 @@ func (bot *Engine) GetCryptocurrencyDepositAddressesByExchange(exchName string) // GetExchangeCryptocurrencyDepositAddress returns the cryptocurrency deposit address for a particular // exchange func (bot *Engine) GetExchangeCryptocurrencyDepositAddress(ctx context.Context, exchName, accountID, chain string, item currency.Code, bypassCache bool) (*deposit.Address, error) { - if bot.DepositAddressManager != nil && bot.DepositAddressManager.IsSynced() && !bypassCache { + if bot.DepositAddressManager != nil && + bot.DepositAddressManager.IsSynced() && + !bypassCache { resp, err := bot.DepositAddressManager.GetDepositAddressByExchangeAndCurrency(exchName, chain, item) return &resp, err } diff --git a/engine/helpers_test.go b/engine/helpers_test.go index 1421ae2c487..0fe7d8b18bb 100644 --- a/engine/helpers_test.go +++ b/engine/helpers_test.go @@ -99,7 +99,7 @@ func CreateTestBot(t *testing.T) *Engine { func TestGetSubsystemsStatus(t *testing.T) { m := (&Engine{}).GetSubsystemsStatus() if len(m) != 15 { - t.Fatalf("subsystem count is wrong expecting: %d but received: %d", 14, len(m)) + t.Fatalf("subsystem count is wrong expecting: %d but received: %d", 15, len(m)) } } @@ -860,7 +860,7 @@ func TestGetCollatedExchangeAccountInfoByCoin(t *testing.T) { Currencies: []account.Balance{ { CurrencyName: currency.BTC, - TotalValue: 100, + Total: 100, Hold: 0, }, }, @@ -875,12 +875,12 @@ func TestGetCollatedExchangeAccountInfoByCoin(t *testing.T) { Currencies: []account.Balance{ { CurrencyName: currency.LTC, - TotalValue: 100, + Total: 100, Hold: 0, }, { CurrencyName: currency.BTC, - TotalValue: 100, + Total: 100, Hold: 0, }, }, @@ -898,7 +898,7 @@ func TestGetCollatedExchangeAccountInfoByCoin(t *testing.T) { t.Fatal("Expected currency was not found in result map") } - if amount.TotalValue != 200 { + if amount.Total != 200 { t.Fatal("Unexpected result") } @@ -1029,10 +1029,10 @@ func (f fakeDepositExchange) GetAvailableTransferChains(_ context.Context, c cur if f.ThrowTransferChainError { return nil, errors.New("unable to get available transfer chains") } - if c.Match(currency.XRP) { + if c.Equal(currency.XRP) { return nil, nil } - if c.Match(currency.USDT) { + if c.Equal(currency.USDT) { return []string{"sol", "btc", "usdt"}, nil } return []string{"BITCOIN"}, nil diff --git a/engine/order_manager.go b/engine/order_manager.go index e2f11fc71ea..443cb96ab14 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -713,8 +713,9 @@ func (m *OrderManager) processOrders() { upsertResponse, err := m.UpsertOrder(&result[z]) if err != nil { log.Error(log.OrderMgr, err) + } else { + requiresProcessing[upsertResponse.OrderDetails.InternalOrderID] = false } - requiresProcessing[upsertResponse.OrderDetails.InternalOrderID] = false } if !exchanges[i].GetBase().GetSupportedFeatures().RESTCapabilities.GetOrder { continue diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 895ec835abd..9dc4ece790a 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -266,12 +266,12 @@ func TestGetByInternalOrderID(t *testing.T) { o, err := m.orderStore.getByInternalOrderID("internalTest") if err != nil { - t.Error(err) + t.Fatal(err) } - if o == nil { + if o == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("Expected a matching order") } - if o.ID != "TestGetByInternalOrderID" { + if o.ID != "TestGetByInternalOrderID" { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Error("Expected to retrieve order") } @@ -422,14 +422,14 @@ func TestStore_modifyOrder(t *testing.T) { _, err = m.orderStore.getByExchangeAndID(testExchange, "fake_order_id") if err == nil { // Expected error, such an order should not exist anymore in the store. - t.Error("Expected error") + t.Fatal("Expected error") } det, err := m.orderStore.getByExchangeAndID(testExchange, "another_fake_order_id") - if det == nil || err != nil { + if det == nil || err != nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("Failed to fetch order details") } - if det.ID != "another_fake_order_id" || det.Price != 16 || det.Amount != 256 { + if det.ID != "another_fake_order_id" || det.Price != 16 || det.Amount != 256 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Errorf( "have (%s,%f,%f), want (%s,%f,%f)", det.ID, det.Price, det.Amount, @@ -521,14 +521,14 @@ func TestCancelOrder(t *testing.T) { func TestGetOrderInfo(t *testing.T) { m := OrdersSetup(t) - _, err := m.GetOrderInfo(context.Background(), "", "", currency.Pair{}, "") + _, err := m.GetOrderInfo(context.Background(), "", "", currency.EMPTYPAIR, "") if err == nil { t.Error("Expected error due to empty order") } var result order.Detail result, err = m.GetOrderInfo(context.Background(), - testExchange, "1337", currency.Pair{}, "") + testExchange, "1337", currency.EMPTYPAIR, "") if err != nil { t.Error(err) } @@ -537,7 +537,7 @@ func TestGetOrderInfo(t *testing.T) { } result, err = m.GetOrderInfo(context.Background(), - testExchange, "1337", currency.Pair{}, "") + testExchange, "1337", currency.EMPTYPAIR, "") if err != nil { t.Error(err) } @@ -1304,6 +1304,12 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { if !unrealised.Equal(decimal.NewFromInt(1)) { t.Errorf("received '%v', expected '%v'", unrealised, 1) } + + o = nil + _, err = o.UpdateOpenPositionUnrealisedPNL("test", asset.Spot, cp, 1, time.Now()) + if !errors.Is(err, ErrNilSubsystem) { + t.Errorf("received '%v', expected '%v'", err, ErrNilSubsystem) + } } func TestSubmitFakeOrder(t *testing.T) { diff --git a/engine/portfolio_manager.go b/engine/portfolio_manager.go index 2e100d0d111..4affcf4a617 100644 --- a/engine/portfolio_manager.go +++ b/engine/portfolio_manager.go @@ -164,28 +164,33 @@ func (m *portfolioManager) seedExchangeAccountInfo(accounts []account.Holdings) for z := range accounts[x].Accounts[y].Currencies { var update bool for i := range currencies { - if accounts[x].Accounts[y].Currencies[z].CurrencyName == currencies[i].CurrencyName { - currencies[i].Hold += accounts[x].Accounts[y].Currencies[z].Hold - currencies[i].TotalValue += accounts[x].Accounts[y].Currencies[z].TotalValue - update = true + if !accounts[x].Accounts[y].Currencies[z].CurrencyName.Equal(currencies[i].CurrencyName) { + continue } + currencies[i].Hold += accounts[x].Accounts[y].Currencies[z].Hold + currencies[i].Total += accounts[x].Accounts[y].Currencies[z].Total + currencies[i].AvailableWithoutBorrow += accounts[x].Accounts[y].Currencies[z].AvailableWithoutBorrow + currencies[i].Free += accounts[x].Accounts[y].Currencies[z].Free + currencies[i].Borrowed += accounts[x].Accounts[y].Currencies[z].Borrowed + update = true } - if update { continue } - currencies = append(currencies, account.Balance{ - CurrencyName: accounts[x].Accounts[y].Currencies[z].CurrencyName, - TotalValue: accounts[x].Accounts[y].Currencies[z].TotalValue, - Hold: accounts[x].Accounts[y].Currencies[z].Hold, + CurrencyName: accounts[x].Accounts[y].Currencies[z].CurrencyName, + Total: accounts[x].Accounts[y].Currencies[z].Total, + Hold: accounts[x].Accounts[y].Currencies[z].Hold, + Free: accounts[x].Accounts[y].Currencies[z].Free, + AvailableWithoutBorrow: accounts[x].Accounts[y].Currencies[z].AvailableWithoutBorrow, + Borrowed: accounts[x].Accounts[y].Currencies[z].Borrowed, }) } } for x := range currencies { currencyName := currencies[x].CurrencyName - total := currencies[x].TotalValue + total := currencies[x].Total if !m.base.ExchangeAddressExists(exchangeName, currencyName) { if total <= 0 { diff --git a/engine/rpcserver.go b/engine/rpcserver.go index bc07f2fdeb1..22788a1671a 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -562,7 +562,7 @@ func (s *RPCServer) GetAccountInfo(ctx context.Context, r *gctrpc.GetAccountInfo return nil, err } - err = checkParams(r.Exchange, exch, assetType, currency.Pair{}) + err = checkParams(r.Exchange, exch, assetType, currency.EMPTYPAIR) if err != nil { return nil, err } @@ -587,7 +587,7 @@ func (s *RPCServer) UpdateAccountInfo(ctx context.Context, r *gctrpc.GetAccountI return nil, err } - err = checkParams(r.Exchange, exch, assetType, currency.Pair{}) + err = checkParams(r.Exchange, exch, assetType, currency.EMPTYPAIR) if err != nil { return nil, err } @@ -606,10 +606,20 @@ func createAccountInfoRequest(h account.Holdings) (*gctrpc.GetAccountInfoRespons var a gctrpc.Account a.Id = h.Accounts[x].ID for _, y := range h.Accounts[x].Currencies { + if y.Total == 0 && + y.Hold == 0 && + y.Free == 0 && + y.AvailableWithoutBorrow == 0 && + y.Borrowed == 0 { + continue + } a.Currencies = append(a.Currencies, &gctrpc.AccountCurrencyInfo{ - Currency: y.CurrencyName.String(), - Hold: y.Hold, - TotalValue: y.TotalValue, + Currency: y.CurrencyName.String(), + TotalValue: y.Total, + Hold: y.Hold, + Free: y.Free, + FreeWithoutBorrow: y.AvailableWithoutBorrow, + Borrowed: y.Borrowed, }) } accounts = append(accounts, &a) @@ -630,7 +640,7 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream return err } - err = checkParams(r.Exchange, exch, assetType, currency.Pair{}) + err = checkParams(r.Exchange, exch, assetType, currency.EMPTYPAIR) if err != nil { return err } @@ -646,7 +656,7 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream for y := range initAcc.Accounts[x].Currencies { subAccounts = append(subAccounts, &gctrpc.AccountCurrencyInfo{ Currency: initAcc.Accounts[x].Currencies[y].CurrencyName.String(), - TotalValue: initAcc.Accounts[x].Currencies[y].TotalValue, + TotalValue: initAcc.Accounts[x].Currencies[y].Total, Hold: initAcc.Accounts[x].Currencies[y].Hold, }) } @@ -698,7 +708,7 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream for y := range acc.Accounts[x].Currencies { subAccounts = append(subAccounts, &gctrpc.AccountCurrencyInfo{ Currency: acc.Accounts[x].Currencies[y].CurrencyName.String(), - TotalValue: acc.Accounts[x].Currencies[y].TotalValue, + TotalValue: acc.Accounts[x].Currencies[y].Total, Hold: acc.Accounts[x].Currencies[y].Hold, }) } @@ -835,7 +845,7 @@ func (s *RPCServer) GetForexProviders(_ context.Context, _ *gctrpc.GetForexProvi Name: providers[x].Name, Enabled: providers[x].Enabled, Verbose: providers[x].Verbose, - RestPollingDelay: providers[x].RESTPollingDelay.String(), + RestPollingDelay: s.Config.Currency.ForeignExchangeUpdateDuration.String(), ApiKey: providers[x].APIKey, ApiKeyLevel: int64(providers[x].APIKeyLvl), PrimaryProvider: providers[x].PrimaryProvider, @@ -1957,7 +1967,7 @@ func (s *RPCServer) SetExchangePair(_ context.Context, r *gctrpc.SetExchangePair return nil, err } - err = checkParams(r.Exchange, exch, a, currency.Pair{}) + err = checkParams(r.Exchange, exch, a, currency.EMPTYPAIR) if err != nil { return nil, err } @@ -4147,9 +4157,15 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture return nil, err } + b := exch.GetBase() + subAccount := b.API.Credentials.Subaccount + var subErr string + if subAccount != "" { + subErr = "for subaccount: " + subAccount + } orders, err := exch.GetFuturesPositions(ctx, a, cp, start, end) if err != nil { - return nil, err + return nil, fmt.Errorf("%w %v", err, subErr) } sort.Slice(orders, func(i, j int) bool { return orders[i].Date.Before(orders[j].Date) @@ -4157,7 +4173,7 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if r.Overwrite { err = s.OrderManager.ClearFuturesTracking(r.Exchange, a, cp) if err != nil { - return nil, err + return nil, fmt.Errorf("%w %v", err, subErr) } } for i := range orders { @@ -4170,11 +4186,10 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture } pos, err := s.OrderManager.GetFuturesPositionsForExchange(r.Exchange, a, cp) if err != nil { - return nil, err + return nil, fmt.Errorf("%w %v", err, subErr) } - b := exch.GetBase() response := &gctrpc.GetFuturesPositionsResponse{ - SubAccount: b.API.Credentials.Subaccount, + SubAccount: subAccount, } var totalRealisedPNL, totalUnrealisedPNL decimal.Decimal for i := range pos { @@ -4204,8 +4219,8 @@ func (s *RPCServer) GetFuturesPositions(ctx context.Context, r *gctrpc.GetFuture if !pos[i].RealisedPNL.IsZero() { details.RealisedPNL = pos[i].RealisedPNL.String() } - if pos[i].Direction != order.UnknownSide { - details.CurrentDirection = pos[i].Direction.String() + if pos[i].LatestDirection != order.UnknownSide { + details.CurrentDirection = pos[i].LatestDirection.String() } if len(pos[i].PNLHistory) > 0 { details.OpeningDate = pos[i].PNLHistory[0].Time.Format(common.SimpleTimeFormatWithTimezone) @@ -4314,55 +4329,171 @@ func (s *RPCServer) GetCollateral(ctx context.Context, r *gctrpc.GetCollateralRe if acc == nil { return nil, fmt.Errorf("%w for %s %s and stored credentials - available subaccounts: %s", errNoAccountInformation, exch.GetName(), r.SubAccount, strings.Join(subAccounts, ",")) } + var spotPairs currency.Pairs + if r.CalculateOffline { + spotPairs, err = exch.GetAvailablePairs(asset.Spot) + if err != nil { + return nil, fmt.Errorf("GetCollateral offline calculation error via GetAvailablePairs %s %s", exch.GetName(), err) + } + } + for i := range acc.Currencies { + total := decimal.NewFromFloat(acc.Currencies[i].Total) + free := decimal.NewFromFloat(acc.Currencies[i].AvailableWithoutBorrow) cal := order.CollateralCalculator{ CalculateOffline: r.CalculateOffline, CollateralCurrency: acc.Currencies[i].CurrencyName, Asset: a, - CollateralAmount: decimal.NewFromFloat(acc.Currencies[i].TotalValue), + FreeCollateral: free, + LockedCollateral: total.Sub(free), } - if r.CalculateOffline && !acc.Currencies[i].CurrencyName.Match(currency.USD) { + if r.CalculateOffline && + !acc.Currencies[i].CurrencyName.Equal(currency.USD) { var tick *ticker.Price - tick, err = exch.FetchTicker(ctx, currency.NewPair(acc.Currencies[i].CurrencyName, currency.USD), asset.Spot) + tickerCurr := currency.NewPair(acc.Currencies[i].CurrencyName, currency.USD) + if !spotPairs.Contains(tickerCurr, true) { + // cannot price currency to calculate collateral + continue + } + tick, err = exch.FetchTicker(ctx, tickerCurr, asset.Spot) if err != nil { - log.Errorf(log.GRPCSys, fmt.Sprintf("GetCollateral offline calculation via FetchTicker %s %s", exch.GetName(), err)) + log.Errorf(log.GRPCSys, fmt.Sprintf("GetCollateral offline calculation error via FetchTicker %s %s", exch.GetName(), err)) continue } if tick.Last == 0 { continue } - cal.CollateralPrice = decimal.NewFromFloat(tick.Last) + cal.USDPrice = decimal.NewFromFloat(tick.Last) } calculators = append(calculators, cal) } - collateral, err := exch.CalculateTotalCollateral(ctx, r.SubAccount, r.CalculateOffline, calculators) + calc := &order.TotalCollateralCalculator{ + SubAccount: r.SubAccount, + CollateralAssets: calculators, + CalculateOffline: r.CalculateOffline, + FetchPositions: true, + } + + collateral, err := exch.CalculateTotalCollateral(ctx, calc) if err != nil { return nil, err } + var collateralDisplayCurrency = " " + collateral.CollateralCurrency.String() result := &gctrpc.GetCollateralResponse{ - SubAccount: subAccount, - TotalCollateral: collateral.TotalCollateral.String(), + SubAccount: subAccount, + CollateralCurrency: collateral.CollateralCurrency.String(), + AvailableCollateral: collateral.AvailableCollateral.String() + collateralDisplayCurrency, + UsedCollateral: collateral.UsedCollateral.String() + collateralDisplayCurrency, + } + if !collateral.CollateralContributedByPositiveSpotBalances.IsZero() { + result.CollateralContributedByPositiveSpotBalances = collateral.CollateralContributedByPositiveSpotBalances.String() + collateralDisplayCurrency + } + if !collateral.TotalValueOfPositiveSpotBalances.IsZero() { + result.TotalValueOfPositiveSpotBalances = collateral.TotalValueOfPositiveSpotBalances.String() + collateralDisplayCurrency + } + if !collateral.AvailableMaintenanceCollateral.IsZero() { + result.MaintenanceCollateral = collateral.AvailableMaintenanceCollateral.String() + collateralDisplayCurrency + } + if !collateral.UnrealisedPNL.IsZero() { + result.UnrealisedPNL = collateral.UnrealisedPNL.String() + } + if collateral.UsedBreakdown != nil { + result.UsedBreakdown = &gctrpc.CollateralUsedBreakdown{} + if !collateral.UsedBreakdown.LockedInStakes.IsZero() { + result.UsedBreakdown.LockedInStakes = collateral.UsedBreakdown.LockedInStakes.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.LockedInNFTBids.IsZero() { + result.UsedBreakdown.LockedIn_NFTBids = collateral.UsedBreakdown.LockedInNFTBids.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.LockedInFeeVoucher.IsZero() { + result.UsedBreakdown.LockedInFeeVoucher = collateral.UsedBreakdown.LockedInFeeVoucher.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.LockedInSpotMarginFundingOffers.IsZero() { + result.UsedBreakdown.LockedInSpotMarginFundingOffers = collateral.UsedBreakdown.LockedInSpotMarginFundingOffers.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.LockedInSpotOrders.IsZero() { + result.UsedBreakdown.LockedInSpotOrders = collateral.UsedBreakdown.LockedInSpotOrders.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.LockedAsCollateral.IsZero() { + result.UsedBreakdown.LockedAsCollateral = collateral.UsedBreakdown.LockedAsCollateral.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.UsedInPositions.IsZero() { + result.UsedBreakdown.UsedInFutures = collateral.UsedBreakdown.UsedInPositions.String() + collateralDisplayCurrency + } + if !collateral.UsedBreakdown.UsedInSpotMarginBorrows.IsZero() { + result.UsedBreakdown.UsedInSpotMargin = collateral.UsedBreakdown.UsedInSpotMarginBorrows.String() + collateralDisplayCurrency + } } if r.IncludeBreakdown { + for i := range collateral.BreakdownOfPositions { + result.PositionBreakdown = append(result.PositionBreakdown, &gctrpc.CollateralByPosition{ + Currency: collateral.BreakdownOfPositions[i].PositionCurrency.String(), + Size: collateral.BreakdownOfPositions[i].Size.String(), + OpenOrderSize: collateral.BreakdownOfPositions[i].OpenOrderSize.String(), + PositionSize: collateral.BreakdownOfPositions[i].PositionSize.String(), + MarkPrice: collateral.BreakdownOfPositions[i].MarkPrice.String() + collateralDisplayCurrency, + RequiredMargin: collateral.BreakdownOfPositions[i].RequiredMargin.String(), + TotalCollateralUsed: collateral.BreakdownOfPositions[i].CollateralUsed.String() + collateralDisplayCurrency, + }) + } for i := range collateral.BreakdownByCurrency { - if collateral.BreakdownByCurrency[i].ScaledValue.IsZero() && !r.IncludeZeroValues { + if collateral.BreakdownByCurrency[i].TotalFunds.IsZero() && !r.IncludeZeroValues { continue } + var originalDisplayCurrency = " " + collateral.BreakdownByCurrency[i].Currency.String() cb := &gctrpc.CollateralForCurrency{ - Currency: collateral.BreakdownByCurrency[i].Currency.String(), - ScaledToCurrency: collateral.BreakdownByCurrency[i].ValueCurrency.String(), + Currency: collateral.BreakdownByCurrency[i].Currency.String(), + ExcludedFromCollateral: collateral.BreakdownByCurrency[i].SkipContribution, + TotalFunds: collateral.BreakdownByCurrency[i].TotalFunds.String() + originalDisplayCurrency, + AvailableForUseAsCollateral: collateral.BreakdownByCurrency[i].AvailableForUseAsCollateral.String() + originalDisplayCurrency, + ApproxFairMarketValue: collateral.BreakdownByCurrency[i].FairMarketValue.String() + collateralDisplayCurrency, + Weighting: collateral.BreakdownByCurrency[i].Weighting.String(), + CollateralContribution: collateral.BreakdownByCurrency[i].CollateralContribution.String() + collateralDisplayCurrency, + ScaledToCurrency: collateral.BreakdownByCurrency[i].ScaledCurrency.String(), + } + if !collateral.BreakdownByCurrency[i].AdditionalCollateralUsed.IsZero() { + cb.AdditionalCollateralUsed = collateral.BreakdownByCurrency[i].AdditionalCollateralUsed.String() + collateralDisplayCurrency } - if collateral.BreakdownByCurrency[i].ScaledValue.GreaterThan(decimal.Zero) { - cb.ScaledCollateral = collateral.BreakdownByCurrency[i].ScaledValue.String() + + if !collateral.BreakdownByCurrency[i].ScaledUsed.IsZero() { + cb.FundsInUse = collateral.BreakdownByCurrency[i].ScaledUsed.String() + collateralDisplayCurrency } - if collateral.BreakdownByCurrency[i].OriginalValue.GreaterThan(decimal.Zero) { - cb.OriginalAmount = collateral.BreakdownByCurrency[i].OriginalValue.String() + if !collateral.BreakdownByCurrency[i].UnrealisedPNL.IsZero() { + cb.Unrealised_PNL = collateral.BreakdownByCurrency[i].UnrealisedPNL.String() + collateralDisplayCurrency } - if collateral.BreakdownByCurrency[i].OriginalValue.IsZero() && - collateral.BreakdownByCurrency[i].ScaledValue.IsZero() { - continue + if collateral.BreakdownByCurrency[i].ScaledUsedBreakdown != nil { + breakDownDisplayCurrency := collateralDisplayCurrency + if collateral.BreakdownByCurrency[i].Weighting.IsZero() && collateral.BreakdownByCurrency[i].FairMarketValue.IsZero() { + // cannot determine value, show in like currency instead + breakDownDisplayCurrency = originalDisplayCurrency + } + cb.UsedBreakdown = &gctrpc.CollateralUsedBreakdown{} + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInStakes.IsZero() { + cb.UsedBreakdown.LockedInStakes = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInStakes.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInNFTBids.IsZero() { + cb.UsedBreakdown.LockedIn_NFTBids = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInNFTBids.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInFeeVoucher.IsZero() { + cb.UsedBreakdown.LockedInFeeVoucher = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInFeeVoucher.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInSpotMarginFundingOffers.IsZero() { + cb.UsedBreakdown.LockedInSpotMarginFundingOffers = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInSpotMarginFundingOffers.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInSpotOrders.IsZero() { + cb.UsedBreakdown.LockedInSpotOrders = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedInSpotOrders.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedAsCollateral.IsZero() { + cb.UsedBreakdown.LockedAsCollateral = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.LockedAsCollateral.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.UsedInPositions.IsZero() { + cb.UsedBreakdown.UsedInFutures = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.UsedInPositions.String() + breakDownDisplayCurrency + } + if !collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.UsedInSpotMarginBorrows.IsZero() { + cb.UsedBreakdown.UsedInSpotMargin = collateral.BreakdownByCurrency[i].ScaledUsedBreakdown.UsedInSpotMarginBorrows.String() + breakDownDisplayCurrency + } } if collateral.BreakdownByCurrency[i].Error != nil { cb.Error = collateral.BreakdownByCurrency[i].Error.Error() diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index d9f0f1b6791..37416228fe5 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -92,6 +92,25 @@ func (f fExchange) GetHistoricCandlesExtended(ctx context.Context, p currency.Pa }, nil } +func (f fExchange) FetchTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { + return &ticker.Price{ + Last: 1337, + High: 1337, + Low: 1337, + Bid: 1337, + Ask: 1337, + Volume: 1337, + QuoteVolume: 1337, + PriceATH: 1337, + Open: 1337, + Close: 1337, + Pair: p, + ExchangeName: f.GetName(), + AssetType: a, + LastUpdated: time.Now(), + }, nil +} + // FetchAccountInfo overrides testExchange's fetch account info function // to do the bare minimum required with no API calls or credentials required func (f fExchange) FetchAccountInfo(_ context.Context, a asset.Item) (account.Holdings, error) { @@ -104,7 +123,11 @@ func (f fExchange) FetchAccountInfo(_ context.Context, a asset.Item) (account.Ho Currencies: []account.Balance{ { CurrencyName: currency.USD, - TotalValue: 1337, + Total: 1337, + }, + { + CurrencyName: currency.BTC, + Total: 13337, }, }, }, @@ -132,23 +155,46 @@ func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp curre } // CalculateTotalCollateral overrides testExchange's CalculateTotalCollateral function -func (f fExchange) CalculateTotalCollateral(context.Context, string, bool, []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { +func (f fExchange) CalculateTotalCollateral(context.Context, *order.TotalCollateralCalculator) (*order.TotalCollateralResponse, error) { return &order.TotalCollateralResponse{ - TotalCollateral: decimal.NewFromInt(1337), + CollateralCurrency: currency.USD, + AvailableMaintenanceCollateral: decimal.NewFromInt(1338), + AvailableCollateral: decimal.NewFromInt(1337), + UsedBreakdown: &order.UsedCollateralBreakdown{ + LockedInStakes: decimal.NewFromInt(3), + LockedInNFTBids: decimal.NewFromInt(3), + LockedInFeeVoucher: decimal.NewFromInt(3), + LockedInSpotMarginFundingOffers: decimal.NewFromInt(3), + LockedInSpotOrders: decimal.NewFromInt(3), + LockedAsCollateral: decimal.NewFromInt(3), + }, BreakdownByCurrency: []order.CollateralByCurrency{ { - Currency: currency.USD, - ScaledValue: decimal.NewFromInt(1330), + Currency: currency.USD, + TotalFunds: decimal.NewFromInt(1330), + CollateralContribution: decimal.NewFromInt(1330), + ScaledCurrency: currency.USD, }, { - Currency: currency.DOGE, - ScaledValue: decimal.NewFromInt(10), - ValueCurrency: currency.USD, + Currency: currency.DOGE, + TotalFunds: decimal.NewFromInt(1000), + ScaledUsed: decimal.NewFromInt(6), + ScaledUsedBreakdown: &order.UsedCollateralBreakdown{ + LockedInStakes: decimal.NewFromInt(1), + LockedInNFTBids: decimal.NewFromInt(1), + LockedInFeeVoucher: decimal.NewFromInt(1), + LockedInSpotMarginFundingOffers: decimal.NewFromInt(1), + LockedInSpotOrders: decimal.NewFromInt(1), + LockedAsCollateral: decimal.NewFromInt(1), + }, + CollateralContribution: decimal.NewFromInt(4), + ScaledCurrency: currency.USD, }, { - Currency: currency.XRP, - ScaledValue: decimal.NewFromInt(-3), - ValueCurrency: currency.USD, + Currency: currency.XRP, + TotalFunds: decimal.NewFromInt(1333333333333337), + CollateralContribution: decimal.NewFromInt(-3), + ScaledCurrency: currency.USD, }, }, }, nil @@ -1522,12 +1568,12 @@ func TestGetDataHistoryJobDetails(t *testing.T) { resp, err := s.GetDataHistoryJobDetails(context.Background(), &gctrpc.GetDataHistoryJobDetailsRequest{Nickname: "TestGetDataHistoryJobDetails", FullDetails: true}) if !errors.Is(err, nil) { - t.Errorf("received %v, expected %v", err, nil) + t.Fatalf("received %v, expected %v", err, nil) } - if resp == nil { + if resp == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected job") } - if !strings.EqualFold(resp.Nickname, "TestGetDataHistoryJobDetails") { + if !strings.EqualFold(resp.Nickname, "TestGetDataHistoryJobDetails") { //nolint:nolintlint,staticcheck // SA5011 Ignore the nil warnings t.Errorf("received %v, expected %v", resp.Nickname, "TestGetDataHistoryJobDetails") } } @@ -1697,14 +1743,14 @@ func TestGetDataHistoryJobSummary(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received %v, expected %v", err, nil) } - if resp == nil { + if resp == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected job") } - if !strings.EqualFold(resp.Nickname, "TestGetDataHistoryJobSummary") { - t.Errorf("received %v, expected %v", "TestGetDataHistoryJobSummary", resp.Nickname) + if !strings.EqualFold(resp.Nickname, "TestGetDataHistoryJobSummary") { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings + t.Fatalf("received %v, expected %v", "TestGetDataHistoryJobSummary", resp.Nickname) } - if resp.ResultSummaries == nil { - t.Errorf("received %v, expected %v", nil, "result summaries slice") + if resp.ResultSummaries == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings + t.Fatalf("received %v, expected %v", nil, "result summaries slice") } } @@ -2090,15 +2136,15 @@ func TestGetFuturesPositions(t *testing.T) { Verbose: true, }) if !errors.Is(err, nil) { - t.Errorf("received '%v', expected '%v'", err, nil) + t.Fatalf("received '%v', expected '%v'", err, nil) } - if r == nil { + if r == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected not nil response") } - if len(r.Positions) != 1 { + if len(r.Positions) != 1 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected 1 position") } - if r.TotalOrders != 1 { + if r.TotalOrders != 1 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected 1 order") } @@ -2177,10 +2223,10 @@ func TestGetCollateral(t *testing.T) { t.Errorf("received '%v', expected '%v'", err, nil) } if len(r.CurrencyBreakdown) != 3 { - t.Error("expected 3 currencies") + t.Errorf("expected 3 currencies, received '%v'", len(r.CurrencyBreakdown)) } - if r.TotalCollateral != "1337" { - t.Error("expected 1337") + if r.AvailableCollateral != "1337 USD" { + t.Errorf("received '%v' expected '1337 USD'", r.AvailableCollateral) } _, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ @@ -2192,4 +2238,15 @@ func TestGetCollateral(t *testing.T) { if !errors.Is(err, order.ErrNotFuturesAsset) { t.Errorf("received '%v', expected '%v'", err, order.ErrNotFuturesAsset) } + + _, err = s.GetCollateral(context.Background(), &gctrpc.GetCollateralRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + IncludeBreakdown: true, + SubAccount: "1337", + CalculateOffline: true, + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } } diff --git a/engine/sync_manager.go b/engine/sync_manager.go index 047acd25a96..8f8b7c5b2d5 100644 --- a/engine/sync_manager.go +++ b/engine/sync_manager.go @@ -42,8 +42,12 @@ var ( ) // setupSyncManager starts a new CurrencyPairSyncer -func setupSyncManager(c *Config, exchangeManager iExchangeManager, remoteConfig *config.RemoteControlConfig, websocketRoutineManagerEnabled bool) (*syncManager, error) { - if !c.SyncOrderbook && !c.SyncTicker && !c.SyncTrades { +func setupSyncManager(c *SyncManagerConfig, exchangeManager iExchangeManager, remoteConfig *config.RemoteControlConfig, websocketRoutineManagerEnabled bool) (*syncManager, error) { + if c == nil { + return nil, fmt.Errorf("%T %w", c, common.ErrNilPointer) + } + + if !c.SynchronizeOrderbook && !c.SynchronizeTicker && !c.SynchronizeTrades { return nil, errNoSyncItemsEnabled } if exchangeManager == nil { @@ -57,12 +61,24 @@ func setupSyncManager(c *Config, exchangeManager iExchangeManager, remoteConfig c.NumWorkers = DefaultSyncerWorkers } - if c.SyncTimeoutREST <= time.Duration(0) { - c.SyncTimeoutREST = DefaultSyncerTimeoutREST + if c.TimeoutREST <= time.Duration(0) { + c.TimeoutREST = DefaultSyncerTimeoutREST } - if c.SyncTimeoutWebsocket <= time.Duration(0) { - c.SyncTimeoutWebsocket = DefaultSyncerTimeoutWebsocket + if c.TimeoutWebsocket <= time.Duration(0) { + c.TimeoutWebsocket = DefaultSyncerTimeoutWebsocket + } + + if c.FiatDisplayCurrency.IsEmpty() { + return nil, fmt.Errorf("FiatDisplayCurrency %w", currency.ErrCurrencyCodeEmpty) + } + + if !c.FiatDisplayCurrency.IsFiatCurrency() { + return nil, fmt.Errorf("%s %w", c.FiatDisplayCurrency, currency.ErrFiatDisplayCurrencyIsNotFiat) + } + + if c.PairFormatDisplay == nil { + return nil, fmt.Errorf("%T %w", c.PairFormatDisplay, common.ErrNilPointer) } s := &syncManager{ @@ -70,27 +86,26 @@ func setupSyncManager(c *Config, exchangeManager iExchangeManager, remoteConfig remoteConfig: remoteConfig, exchangeManager: exchangeManager, websocketRoutineManagerEnabled: websocketRoutineManagerEnabled, + fiatDisplayCurrency: c.FiatDisplayCurrency, + delimiter: c.PairFormatDisplay.Delimiter, + uppercase: c.PairFormatDisplay.Uppercase, + tickerBatchLastRequested: make(map[string]time.Time), } - s.tickerBatchLastRequested = make(map[string]time.Time) - log.Debugf(log.SyncMgr, "Exchange currency pair syncer config: continuous: %v ticker: %v"+ " orderbook: %v trades: %v workers: %v verbose: %v timeout REST: %v"+ " timeout Websocket: %v", - s.config.SyncContinuously, s.config.SyncTicker, s.config.SyncOrderbook, - s.config.SyncTrades, s.config.NumWorkers, s.config.Verbose, s.config.SyncTimeoutREST, - s.config.SyncTimeoutWebsocket) + s.config.SynchronizeContinuously, s.config.SynchronizeTicker, s.config.SynchronizeOrderbook, + s.config.SynchronizeTrades, s.config.NumWorkers, s.config.Verbose, s.config.TimeoutREST, + s.config.TimeoutWebsocket) s.inService.Add(1) return s, nil } // IsRunning safely checks whether the subsystem is running func (m *syncManager) IsRunning() bool { - if m == nil { - return false - } - return atomic.LoadInt32(&m.started) == 1 + return m != nil && atomic.LoadInt32(&m.started) == 1 } // Start runs the subsystem @@ -169,13 +184,13 @@ func (m *syncManager) Start() error { IsUsingREST: usingREST || !wsAssetSupported, IsUsingWebsocket: usingWebsocket && wsAssetSupported, } - if m.config.SyncTicker { + if m.config.SynchronizeTicker { c.Ticker = sBase } - if m.config.SyncOrderbook { + if m.config.SynchronizeOrderbook { c.Orderbook = sBase } - if m.config.SyncTrades { + if m.config.SynchronizeTrades { c.Trade = sBase } @@ -199,7 +214,7 @@ func (m *syncManager) Start() error { log.Debugf(log.SyncMgr, "Exchange CurrencyPairSyncer initial sync took %v [%v sync items].", completedTime.Sub(m.initSyncStartTime), createdCounter) - if !m.config.SyncContinuously { + if !m.config.SynchronizeContinuously { log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer stopping.") err := m.Stop() if err != nil { @@ -210,7 +225,7 @@ func (m *syncManager) Start() error { } }() - if atomic.LoadInt32(&m.initSyncCompleted) == 1 && !m.config.SyncContinuously { + if atomic.LoadInt32(&m.initSyncCompleted) == 1 && !m.config.SynchronizeContinuously { return nil } @@ -267,7 +282,7 @@ func (m *syncManager) add(c *currencyPairSyncAgent) { m.mux.Lock() defer m.mux.Unlock() - if m.config.SyncTicker { + if m.config.SynchronizeTicker { if m.config.Verbose { log.Debugf(log.SyncMgr, "%s: Added ticker sync item %v: using websocket: %v using REST: %v", @@ -280,7 +295,7 @@ func (m *syncManager) add(c *currencyPairSyncAgent) { } } - if m.config.SyncOrderbook { + if m.config.SynchronizeOrderbook { if m.config.Verbose { log.Debugf(log.SyncMgr, "%s: Added orderbook sync item %v: using websocket: %v using REST: %v", @@ -293,7 +308,7 @@ func (m *syncManager) add(c *currencyPairSyncAgent) { } } - if m.config.SyncTrades { + if m.config.SynchronizeTrades { if m.config.Verbose { log.Debugf(log.SyncMgr, "%s: Added trade sync item %v: using websocket: %v using REST: %v", @@ -367,15 +382,15 @@ func (m *syncManager) Update(exchangeName string, p currency.Pair, a asset.Item, switch syncType { case SyncItemOrderbook: - if !m.config.SyncOrderbook { + if !m.config.SynchronizeOrderbook { return nil } case SyncItemTicker: - if !m.config.SyncTicker { + if !m.config.SynchronizeTicker { return nil } case SyncItemTrade: - if !m.config.SyncTrades { + if !m.config.SynchronizeTrades { return nil } default: @@ -517,15 +532,15 @@ func (m *syncManager) worker() { IsUsingWebsocket: usingWebsocket && wsAssetSupported, } - if m.config.SyncTicker { + if m.config.SynchronizeTicker { c.Ticker = sBase } - if m.config.SyncOrderbook { + if m.config.SynchronizeOrderbook { c.Orderbook = sBase } - if m.config.SyncTrades { + if m.config.SynchronizeTrades { c.Trade = sBase } @@ -542,13 +557,13 @@ func (m *syncManager) worker() { switchedToRest = false } - if m.config.SyncOrderbook { + if m.config.SynchronizeOrderbook { if !m.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemOrderbook) { if c.Orderbook.LastUpdated.IsZero() || - (time.Since(c.Orderbook.LastUpdated) > m.config.SyncTimeoutREST && c.Orderbook.IsUsingREST) || - (time.Since(c.Orderbook.LastUpdated) > m.config.SyncTimeoutWebsocket && c.Orderbook.IsUsingWebsocket) { + (time.Since(c.Orderbook.LastUpdated) > m.config.TimeoutREST && c.Orderbook.IsUsingREST) || + (time.Since(c.Orderbook.LastUpdated) > m.config.TimeoutWebsocket && c.Orderbook.IsUsingWebsocket) { if c.Orderbook.IsUsingWebsocket { - if time.Since(c.Created) < m.config.SyncTimeoutWebsocket { + if time.Since(c.Created) < m.config.TimeoutWebsocket { continue } if supportsREST { @@ -560,7 +575,7 @@ func (m *syncManager) worker() { c.Exchange, m.FormatCurrency(c.Pair).String(), strings.ToUpper(c.AssetType.String()), - m.config.SyncTimeoutWebsocket, + m.config.TimeoutWebsocket, ) switchedToRest = true m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemOrderbook, false) @@ -586,13 +601,13 @@ func (m *syncManager) worker() { } } - if m.config.SyncTicker { + if m.config.SynchronizeTicker { if !m.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemTicker) { if c.Ticker.LastUpdated.IsZero() || - (time.Since(c.Ticker.LastUpdated) > m.config.SyncTimeoutREST && c.Ticker.IsUsingREST) || - (time.Since(c.Ticker.LastUpdated) > m.config.SyncTimeoutWebsocket && c.Ticker.IsUsingWebsocket) { + (time.Since(c.Ticker.LastUpdated) > m.config.TimeoutREST && c.Ticker.IsUsingREST) || + (time.Since(c.Ticker.LastUpdated) > m.config.TimeoutWebsocket && c.Ticker.IsUsingWebsocket) { if c.Ticker.IsUsingWebsocket { - if time.Since(c.Created) < m.config.SyncTimeoutWebsocket { + if time.Since(c.Created) < m.config.TimeoutWebsocket { continue } @@ -605,7 +620,7 @@ func (m *syncManager) worker() { c.Exchange, m.FormatCurrency(enabledPairs[i]).String(), strings.ToUpper(c.AssetType.String()), - m.config.SyncTimeoutWebsocket, + m.config.TimeoutWebsocket, ) switchedToRest = true m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTicker, false) @@ -625,7 +640,7 @@ func (m *syncManager) worker() { } m.mux.Unlock() - if batchLastDone.IsZero() || time.Since(batchLastDone) > m.config.SyncTimeoutREST { + if batchLastDone.IsZero() || time.Since(batchLastDone) > m.config.TimeoutREST { m.mux.Lock() if m.config.Verbose { log.Debugf(log.SyncMgr, "Initialising %s REST ticker batching", exchangeName) @@ -666,9 +681,9 @@ func (m *syncManager) worker() { } } - if m.config.SyncTrades { + if m.config.SynchronizeTrades { if !m.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemTrade) { - if c.Trade.LastUpdated.IsZero() || time.Since(c.Trade.LastUpdated) > m.config.SyncTimeoutREST { + if c.Trade.LastUpdated.IsZero() || time.Since(c.Trade.LastUpdated) > m.config.TimeoutREST { m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, true) err := m.Update(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, nil) if err != nil { @@ -693,12 +708,14 @@ func printCurrencyFormat(price float64, displayCurrency currency.Code) string { return fmt.Sprintf("%s%.8f", displaySymbol, price) } -func printConvertCurrencyFormat(origCurrency currency.Code, origPrice float64, displayCurrency currency.Code) string { - conv, err := currency.ConvertCurrency(origPrice, - origCurrency, - displayCurrency) - if err != nil { - log.Errorf(log.SyncMgr, "Failed to convert currency: %s", err) +func printConvertCurrencyFormat(origPrice float64, origCurrency, displayCurrency currency.Code) string { + var conv float64 + if origPrice > 0 { + var err error + conv, err = currency.ConvertFiat(origPrice, origCurrency, displayCurrency) + if err != nil { + log.Errorf(log.SyncMgr, "Failed to convert currency: %s", err) + } } displaySymbol, err := currency.GetSymbolByCurrencyName(displayCurrency) @@ -745,7 +762,7 @@ func (m *syncManager) PrintTickerSummary(result *ticker.Price, protocol string, _ = stats.Add(result.ExchangeName, result.Pair, result.AssetType, result.Last, result.Volume) if result.Pair.Quote.IsFiatCurrency() && - result.Pair.Quote != m.fiatDisplayCurrency && + !result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty() { origCurrency := result.Pair.Quote.Upper() log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f", @@ -753,15 +770,15 @@ func (m *syncManager) PrintTickerSummary(result *ticker.Price, protocol string, protocol, m.FormatCurrency(result.Pair), strings.ToUpper(result.AssetType.String()), - printConvertCurrencyFormat(origCurrency, result.Last, m.fiatDisplayCurrency), - printConvertCurrencyFormat(origCurrency, result.Ask, m.fiatDisplayCurrency), - printConvertCurrencyFormat(origCurrency, result.Bid, m.fiatDisplayCurrency), - printConvertCurrencyFormat(origCurrency, result.High, m.fiatDisplayCurrency), - printConvertCurrencyFormat(origCurrency, result.Low, m.fiatDisplayCurrency), + printConvertCurrencyFormat(result.Last, origCurrency, m.fiatDisplayCurrency), + printConvertCurrencyFormat(result.Ask, origCurrency, m.fiatDisplayCurrency), + printConvertCurrencyFormat(result.Bid, origCurrency, m.fiatDisplayCurrency), + printConvertCurrencyFormat(result.High, origCurrency, m.fiatDisplayCurrency), + printConvertCurrencyFormat(result.Low, origCurrency, m.fiatDisplayCurrency), result.Volume) } else { if result.Pair.Quote.IsFiatCurrency() && - result.Pair.Quote == m.fiatDisplayCurrency && + result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty() { log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f", result.ExchangeName, @@ -838,11 +855,15 @@ func (m *syncManager) PrintOrderbookSummary(result *orderbook.Base, protocol str var bidValueResult, askValueResult string switch { - case result.Pair.Quote.IsFiatCurrency() && result.Pair.Quote != m.fiatDisplayCurrency && !m.fiatDisplayCurrency.IsEmpty(): + case result.Pair.Quote.IsFiatCurrency() && !result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty(): origCurrency := result.Pair.Quote.Upper() - bidValueResult = printConvertCurrencyFormat(origCurrency, bidsValue, m.fiatDisplayCurrency) - askValueResult = printConvertCurrencyFormat(origCurrency, asksValue, m.fiatDisplayCurrency) - case result.Pair.Quote.IsFiatCurrency() && result.Pair.Quote == m.fiatDisplayCurrency && !m.fiatDisplayCurrency.IsEmpty(): + if bidsValue > 0 { + bidValueResult = printConvertCurrencyFormat(bidsValue, origCurrency, m.fiatDisplayCurrency) + } + if asksValue > 0 { + askValueResult = printConvertCurrencyFormat(asksValue, origCurrency, m.fiatDisplayCurrency) + } + case result.Pair.Quote.IsFiatCurrency() && result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty(): bidValueResult = printCurrencyFormat(bidsValue, m.fiatDisplayCurrency) askValueResult = printCurrencyFormat(asksValue, m.fiatDisplayCurrency) default: diff --git a/engine/sync_manager_test.go b/engine/sync_manager_test.go index ac9635913bc..fe8a9a9010e 100644 --- a/engine/sync_manager_test.go +++ b/engine/sync_manager_test.go @@ -14,22 +14,42 @@ import ( func TestSetupSyncManager(t *testing.T) { t.Parallel() - _, err := setupSyncManager(&Config{}, nil, nil, false) + _, err := setupSyncManager(nil, nil, nil, false) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("error '%v', expected '%v'", err, common.ErrNilPointer) + } + + _, err = setupSyncManager(&SyncManagerConfig{}, nil, nil, false) if !errors.Is(err, errNoSyncItemsEnabled) { t.Errorf("error '%v', expected '%v'", err, errNoSyncItemsEnabled) } - _, err = setupSyncManager(&Config{SyncTrades: true}, nil, nil, false) + _, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true}, nil, nil, false) if !errors.Is(err, errNilExchangeManager) { t.Errorf("error '%v', expected '%v'", err, errNilExchangeManager) } - _, err = setupSyncManager(&Config{SyncTrades: true}, &ExchangeManager{}, nil, false) + _, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true}, &ExchangeManager{}, nil, false) if !errors.Is(err, errNilConfig) { t.Errorf("error '%v', expected '%v'", err, errNilConfig) } - m, err := setupSyncManager(&Config{SyncTrades: true}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) + _, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) + if !errors.Is(err, currency.ErrCurrencyCodeEmpty) { + t.Errorf("error '%v', expected '%v'", err, currency.ErrCurrencyCodeEmpty) + } + + _, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.BTC}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) + if !errors.Is(err, currency.ErrFiatDisplayCurrencyIsNotFiat) { + t.Errorf("error '%v', expected '%v'", err, currency.ErrFiatDisplayCurrencyIsNotFiat) + } + + _, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.USD}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("error '%v', expected '%v'", err, common.ErrNilPointer) + } + + m, err := setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: ¤cy.PairFormat{}}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -40,7 +60,7 @@ func TestSetupSyncManager(t *testing.T) { func TestSyncManagerStart(t *testing.T) { t.Parallel() - m, err := setupSyncManager(&Config{SyncTrades: true}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) + m, err := setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: ¤cy.PairFormat{}}, &ExchangeManager{}, &config.RemoteControlConfig{}, true) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -52,7 +72,7 @@ func TestSyncManagerStart(t *testing.T) { exch.SetDefaults() em.Add(exch) m.exchangeManager = em - m.config.SyncContinuously = true + m.config.SynchronizeContinuously = true err = m.Start() if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) @@ -85,7 +105,7 @@ func TestSyncManagerStop(t *testing.T) { } exch.SetDefaults() em.Add(exch) - m, err = setupSyncManager(&Config{SyncTrades: true, SyncContinuously: true}, em, &config.RemoteControlConfig{}, false) + m, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, SynchronizeContinuously: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: ¤cy.PairFormat{}}, em, &config.RemoteControlConfig{}, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -115,7 +135,7 @@ func TestPrintCurrencyFormat(t *testing.T) { func TestPrintConvertCurrencyFormat(t *testing.T) { t.Parallel() - c := printConvertCurrencyFormat(currency.BTC, 1337, currency.USD) + c := printConvertCurrencyFormat(1337, currency.BTC, currency.USD) if c == "" { t.Error("expected formatted currency") } @@ -133,7 +153,7 @@ func TestPrintTickerSummary(t *testing.T) { } exch.SetDefaults() em.Add(exch) - m, err = setupSyncManager(&Config{SyncTrades: true, SyncContinuously: true}, em, &config.RemoteControlConfig{}, false) + m, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, SynchronizeContinuously: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: ¤cy.PairFormat{}}, em, &config.RemoteControlConfig{}, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -172,7 +192,7 @@ func TestPrintOrderbookSummary(t *testing.T) { } exch.SetDefaults() em.Add(exch) - m, err = setupSyncManager(&Config{SyncTrades: true, SyncContinuously: true}, em, &config.RemoteControlConfig{}, false) + m, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, SynchronizeContinuously: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: ¤cy.PairFormat{}}, em, &config.RemoteControlConfig{}, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } diff --git a/engine/sync_manager_types.go b/engine/sync_manager_types.go index 854eac89399..e0656e04611 100644 --- a/engine/sync_manager_types.go +++ b/engine/sync_manager_types.go @@ -30,16 +30,18 @@ type currencyPairSyncAgent struct { Trade syncBase } -// Config stores the currency pair config -type Config struct { - SyncTicker bool - SyncOrderbook bool - SyncTrades bool - SyncContinuously bool - SyncTimeoutREST time.Duration - SyncTimeoutWebsocket time.Duration - NumWorkers int - Verbose bool +// SyncManagerConfig stores the currency pair synchronization manager config +type SyncManagerConfig struct { + SynchronizeTicker bool + SynchronizeOrderbook bool + SynchronizeTrades bool + SynchronizeContinuously bool + TimeoutREST time.Duration + TimeoutWebsocket time.Duration + NumWorkers int + FiatDisplayCurrency currency.Code + PairFormatDisplay *currency.PairFormat + Verbose bool } // syncManager stores the exchange currency pair syncer object @@ -60,6 +62,6 @@ type syncManager struct { tickerBatchLastRequested map[string]time.Time remoteConfig *config.RemoteControlConfig - config Config + config SyncManagerConfig exchangeManager iExchangeManager } diff --git a/engine/websocketroutine_manager.go b/engine/websocketroutine_manager.go index 3e3ffc9ba91..358a70da2ef 100644 --- a/engine/websocketroutine_manager.go +++ b/engine/websocketroutine_manager.go @@ -5,7 +5,6 @@ import ( "sync/atomic" "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/account" "github.com/thrasher-corp/gocryptotrader/exchanges/fill" @@ -18,7 +17,7 @@ import ( ) // setupWebsocketRoutineManager creates a new websocket routine manager -func setupWebsocketRoutineManager(exchangeManager iExchangeManager, orderManager iOrderManager, syncer iCurrencyPairSyncer, cfg *config.CurrencyConfig, verbose bool) (*websocketRoutineManager, error) { +func setupWebsocketRoutineManager(exchangeManager iExchangeManager, orderManager iOrderManager, syncer iCurrencyPairSyncer, cfg *currency.Config, verbose bool) (*websocketRoutineManager, error) { if exchangeManager == nil { return nil, errNilExchangeManager } diff --git a/engine/websocketroutine_manager_test.go b/engine/websocketroutine_manager_test.go index 0ca86bbca26..34cf56a9edc 100644 --- a/engine/websocketroutine_manager_test.go +++ b/engine/websocketroutine_manager_test.go @@ -5,7 +5,6 @@ import ( "sync" "testing" - "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -34,12 +33,12 @@ func TestWebsocketRoutineManagerSetup(t *testing.T) { t.Errorf("error '%v', expected '%v'", err, errNilCurrencyConfig) } - _, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, true) + _, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, ¤cy.Config{}, true) if !errors.Is(err, errNilCurrencyPairFormat) { t.Errorf("error '%v', expected '%v'", err, errNilCurrencyPairFormat) } - m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, false) + m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, ¤cy.Config{}, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -54,7 +53,7 @@ func TestWebsocketRoutineManagerStart(t *testing.T) { if !errors.Is(err, ErrNilSubsystem) { t.Errorf("error '%v', expected '%v'", err, ErrNilSubsystem) } - cfg := &config.CurrencyConfig{CurrencyPairFormat: &config.CurrencyPairFormatConfig{ + cfg := ¤cy.Config{CurrencyPairFormat: ¤cy.PairFormat{ Uppercase: false, Delimiter: "-", }} @@ -78,7 +77,7 @@ func TestWebsocketRoutineManagerIsRunning(t *testing.T) { t.Error("expected false") } - m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, false) + m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, ¤cy.Config{}, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -102,7 +101,7 @@ func TestWebsocketRoutineManagerStop(t *testing.T) { t.Errorf("error '%v', expected '%v'", err, ErrNilSubsystem) } - m, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, false) + m, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, ¤cy.Config{}, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -140,7 +139,7 @@ func TestWebsocketRoutineManagerHandleData(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } - cfg := &config.CurrencyConfig{CurrencyPairFormat: &config.CurrencyPairFormatConfig{ + cfg := ¤cy.Config{CurrencyPairFormat: ¤cy.PairFormat{ Uppercase: false, Delimiter: "-", }} diff --git a/engine/websocketroutine_manager_types.go b/engine/websocketroutine_manager_types.go index 33acb92acd4..c1fc7b58c2d 100644 --- a/engine/websocketroutine_manager_types.go +++ b/engine/websocketroutine_manager_types.go @@ -4,7 +4,7 @@ import ( "errors" "sync" - "github.com/thrasher-corp/gocryptotrader/config" + "github.com/thrasher-corp/gocryptotrader/currency" ) // websocketRoutineManager is used to process websocket updates from a unified location @@ -14,7 +14,7 @@ type websocketRoutineManager struct { exchangeManager iExchangeManager orderManager iOrderManager syncer iCurrencyPairSyncer - currencyConfig *config.CurrencyConfig + currencyConfig *currency.Config shutdown chan struct{} wg sync.WaitGroup } diff --git a/exchanges/account/account.go b/exchanges/account/account.go index bd3b8a9ca18..4ca3b0c1984 100644 --- a/exchanges/account/account.go +++ b/exchanges/account/account.go @@ -16,6 +16,29 @@ func init() { service.mux = dispatch.GetNewMux() } +// CollectBalances converts a map of sub-account balances into a slice +func CollectBalances(accountBalances map[string][]Balance, assetType asset.Item) (accounts []SubAccount, err error) { + if accountBalances == nil { + return nil, errAccountBalancesIsNil + } + + if !assetType.IsValid() { + return nil, fmt.Errorf("%s, %w", assetType, asset.ErrNotSupported) + } + + accounts = make([]SubAccount, len(accountBalances)) + i := 0 + for accountID, balances := range accountBalances { + accounts[i] = SubAccount{ + ID: accountID, + AssetType: assetType, + Currencies: balances, + } + i++ + } + return +} + // SubscribeToExchangeAccount subcribes to your exchange account func SubscribeToExchangeAccount(exchange string) (dispatch.Pipe, error) { exchange = strings.ToLower(exchange) @@ -93,9 +116,3 @@ func (s *Service) Update(a *Holdings) error { return s.mux.Publish([]uuid.UUID{acc.ID}, acc.h) } - -// Available returns the amount you can use immediately. E.g. if you have $100, but $20 -// are held (locked) because of a limit buy order, your available balance is $80. -func (b Balance) Available() float64 { - return b.TotalValue - b.Hold -} diff --git a/exchanges/account/account_test.go b/exchanges/account/account_test.go index 16c9ceffb40..f5fd46716ce 100644 --- a/exchanges/account/account_test.go +++ b/exchanges/account/account_test.go @@ -10,6 +10,47 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) +func TestCollectBalances(t *testing.T) { + accounts, err := CollectBalances( + map[string][]Balance{ + "someAccountID": { + {CurrencyName: currency.BTC, Total: 40000, Hold: 1}, + }, + }, + asset.Spot, + ) + subAccount := accounts[0] + balance := subAccount.Currencies[0] + if subAccount.ID != "someAccountID" { + t.Error("subAccount ID not set correctly") + } + if subAccount.AssetType != asset.Spot { + t.Error("subAccount AssetType not set correctly") + } + if balance.CurrencyName != currency.BTC || balance.Total != 40000 || balance.Hold != 1 { + t.Error("subAccount currency balance not set correctly") + } + if err != nil { + t.Error("err is not expected") + } + + accounts, err = CollectBalances(map[string][]Balance{}, asset.Spot) + if len(accounts) != 0 { + t.Error("accounts should be empty") + } + if err != nil { + t.Error("err is not expected") + } + + accounts, err = CollectBalances(nil, asset.Spot) + if len(accounts) != 0 { + t.Error("accounts should be empty") + } + if err == nil { + t.Errorf("expecting err %s", errAccountBalancesIsNil.Error()) + } +} + func TestHoldings(t *testing.T) { err := dispatch.Start(dispatch.DefaultMaxWorkers, dispatch.DefaultJobsLimit) if err != nil { @@ -42,7 +83,7 @@ func TestHoldings(t *testing.T) { Currencies: []Balance{ { CurrencyName: currency.BTC, - TotalValue: 100, + Total: 100, Hold: 20, }, }, @@ -81,9 +122,9 @@ func TestHoldings(t *testing.T) { u.Accounts[0].Currencies[0].CurrencyName) } - if u.Accounts[0].Currencies[0].TotalValue != 100 { + if u.Accounts[0].Currencies[0].Total != 100 { t.Errorf("expecting 100 but received %f", - u.Accounts[0].Currencies[0].TotalValue) + u.Accounts[0].Currencies[0].Total) } if u.Accounts[0].Currencies[0].Hold != 20 { @@ -122,7 +163,7 @@ func TestHoldings(t *testing.T) { Currencies: []Balance{ { CurrencyName: currency.BTC, - TotalValue: 100000, + Total: 100000, Hold: 20, }, }, @@ -134,29 +175,3 @@ func TestHoldings(t *testing.T) { wg.Wait() } - -func TestBalance_Available(t *testing.T) { - t.Parallel() - - b := Balance{ - CurrencyName: currency.BTC, - TotalValue: 16, - Hold: 0, - } - - if have := b.Available(); have != 16 { - t.Errorf("have %f, want 16", have) - } - - b.Hold = 8 - - if have := b.Available(); have != 8 { - t.Errorf("have %f, want 8", have) - } - - b.Hold = 16 - - if have := b.Available(); have != 0 { - t.Errorf("have %f, want 0", have) - } -} diff --git a/exchanges/account/account_types.go b/exchanges/account/account_types.go index 682b8a03a94..bd9c2eaff3e 100644 --- a/exchanges/account/account_types.go +++ b/exchanges/account/account_types.go @@ -1,6 +1,7 @@ package account import ( + "errors" "sync" "github.com/gofrs/uuid" @@ -11,7 +12,8 @@ import ( // Vars for the ticker package var ( - service *Service + service *Service + errAccountBalancesIsNil = errors.New("account balances is nil") ) // Service holds ticker information for each individual exchange @@ -43,9 +45,12 @@ type SubAccount struct { // Balance is a sub type to store currency name and individual totals type Balance struct { - CurrencyName currency.Code - TotalValue float64 - Hold float64 + CurrencyName currency.Code + Total float64 + Hold float64 + Free float64 + AvailableWithoutBorrow float64 + Borrowed float64 } // Change defines incoming balance change on currency holdings diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 1e84e1bee91..0109fcf5fa8 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -73,8 +73,11 @@ func (a *Alphapoint) SetDefaults() { }, } - a.Requester = request.New(a.Name, + a.Requester, err = request.New(a.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } } // FetchTradablePairs returns a list of the exchanges tradable pairs @@ -100,12 +103,12 @@ func (a *Alphapoint) UpdateAccountInfo(ctx context.Context, assetType asset.Item var balances []account.Balance for i := range acc.Currencies { - var balance account.Balance - balance.CurrencyName = currency.NewCode(acc.Currencies[i].Name) - balance.TotalValue = float64(acc.Currencies[i].Balance) - balance.Hold = float64(acc.Currencies[i].Hold) - - balances = append(balances, balance) + balances = append(balances, account.Balance{ + CurrencyName: currency.NewCode(acc.Currencies[i].Name), + Total: float64(acc.Currencies[i].Balance), + Hold: float64(acc.Currencies[i].Hold), + Free: float64(acc.Currencies[i].Balance) - float64(acc.Currencies[i].Hold), + }) } response.Accounts = append(response.Accounts, account.SubAccount{ diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index bd6da4a5001..d2038c111cb 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -1174,7 +1174,7 @@ func (b *Binance) FetchSpotExchangeLimits(ctx context.Context) ([]order.MinMaxLe assets = append(assets, asset.Spot) case "MARGIN": assets = append(assets, asset.Margin) - case "LEVERAGED": // leveraged tokens not available for spot trading + case "LEVERAGED", "TRD_GRP_003": // unused permissions default: return nil, fmt.Errorf("unhandled asset type for exchange limits loading %s", spot.Symbols[x].Permissions[y]) diff --git a/exchanges/binance/binance_mock_test.go b/exchanges/binance/binance_mock_test.go index cefeb9ae885..2e54e6bc893 100644 --- a/exchanges/binance/binance_mock_test.go +++ b/exchanges/binance/binance_mock_test.go @@ -46,7 +46,10 @@ func TestMain(m *testing.M) { if err != nil { log.Fatalf("Mock server error %s", err) } - b.HTTPClient = newClient + err = b.SetHTTPClient(newClient) + if err != nil { + log.Fatalf("Mock server error %s", err) + } endpointMap := b.API.Endpoints.GetURLMap() for k := range endpointMap { err = b.API.Endpoints.SetRunning(k, serverDetails) diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index a1fd153b3cc..032368c6d10 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -230,7 +230,7 @@ func TestUGetMarkPrice(t *testing.T) { if err != nil { t.Error(err) } - _, err = b.UGetMarkPrice(context.Background(), currency.Pair{}) + _, err = b.UGetMarkPrice(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -254,7 +254,7 @@ func TestU24HTickerPriceChangeStats(t *testing.T) { if err != nil { t.Error(err) } - _, err = b.U24HTickerPriceChangeStats(context.Background(), currency.Pair{}) + _, err = b.U24HTickerPriceChangeStats(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -266,7 +266,7 @@ func TestUSymbolPriceTicker(t *testing.T) { if err != nil { t.Error(err) } - _, err = b.USymbolPriceTicker(context.Background(), currency.Pair{}) + _, err = b.USymbolPriceTicker(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -278,7 +278,7 @@ func TestUSymbolOrderbookTicker(t *testing.T) { if err != nil { t.Error(err) } - _, err = b.USymbolOrderbookTicker(context.Background(), currency.Pair{}) + _, err = b.USymbolOrderbookTicker(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -370,7 +370,7 @@ func TestUCompositeIndexInfo(t *testing.T) { if err != nil { t.Error(err) } - _, err = b.UCompositeIndexInfo(context.Background(), currency.Pair{}) + _, err = b.UCompositeIndexInfo(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -489,7 +489,7 @@ func TestUAllAccountOrders(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test: api keys not set") } - _, err := b.UAllAccountOrders(context.Background(), currency.Pair{}, 0, 0, time.Time{}, time.Time{}) + _, err := b.UAllAccountOrders(context.Background(), currency.EMPTYPAIR, 0, 0, time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -592,7 +592,7 @@ func TestUAccountIncomeHistory(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test: api keys not set") } - _, err := b.UAccountIncomeHistory(context.Background(), currency.Pair{}, "", 5, time.Now().Add(-time.Hour*48), time.Now()) + _, err := b.UAccountIncomeHistory(context.Background(), currency.EMPTYPAIR, "", 5, time.Now().Add(-time.Hour*48), time.Now()) if err != nil { t.Error(err) } @@ -764,7 +764,7 @@ func TestGetFuturesSwapTickerChangeStats(t *testing.T) { if err != nil { t.Error(err) } - _, err = b.GetFuturesSwapTickerChangeStats(context.Background(), currency.Pair{}, "") + _, err = b.GetFuturesSwapTickerChangeStats(context.Background(), currency.EMPTYPAIR, "") if err != nil { t.Error(err) } @@ -810,7 +810,7 @@ func TestGetFuturesSymbolPriceTicker(t *testing.T) { func TestGetFuturesOrderbookTicker(t *testing.T) { t.Parallel() - _, err := b.GetFuturesOrderbookTicker(context.Background(), currency.Pair{}, "") + _, err := b.GetFuturesOrderbookTicker(context.Background(), currency.EMPTYPAIR, "") if err != nil { t.Error(err) } @@ -822,7 +822,7 @@ func TestGetFuturesOrderbookTicker(t *testing.T) { func TestGetFuturesLiquidationOrders(t *testing.T) { t.Parallel() - _, err := b.GetFuturesLiquidationOrders(context.Background(), currency.Pair{}, "", 0, time.Time{}, time.Time{}) + _, err := b.GetFuturesLiquidationOrders(context.Background(), currency.EMPTYPAIR, "", 0, time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -1124,7 +1124,7 @@ func TestFuturesIncomeHistory(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test: api keys not set") } - _, err := b.FuturesIncomeHistory(context.Background(), currency.Pair{}, "TRANSFER", time.Time{}, time.Time{}, 5) + _, err := b.FuturesIncomeHistory(context.Background(), currency.EMPTYPAIR, "TRANSFER", time.Time{}, time.Time{}, 5) if err != nil { t.Error(err) } @@ -1135,7 +1135,7 @@ func TestFuturesForceOrders(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test: api keys not set") } - _, err := b.FuturesForceOrders(context.Background(), currency.Pair{}, "ADL", time.Time{}, time.Time{}) + _, err := b.FuturesForceOrders(context.Background(), currency.EMPTYPAIR, "ADL", time.Time{}, time.Time{}) if err != nil { t.Error(err) } @@ -1161,7 +1161,7 @@ func TestFuturesPositionsADLEstimate(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test: api keys not set") } - _, err := b.FuturesPositionsADLEstimate(context.Background(), currency.Pair{}) + _, err := b.FuturesPositionsADLEstimate(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -1190,7 +1190,7 @@ func TestGetExchangeInfo(t *testing.T) { t.Error(err) } if mockTests { - serverTime := time.Date(2021, 1, 27, 2, 43, 18, int(593*time.Millisecond), time.UTC) + serverTime := time.Date(2022, 2, 25, 3, 50, 40, int(601*time.Millisecond), time.UTC) if !info.Servertime.Equal(serverTime) { t.Errorf("Expected %v, got %v", serverTime, info.Servertime) } @@ -1341,7 +1341,7 @@ func TestOpenOrders(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := b.OpenOrders(context.Background(), currency.Pair{}) + _, err := b.OpenOrders(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -2797,3 +2797,14 @@ func TestFormatUSDTMarginedFuturesPair(t *testing.T) { t.Errorf("received '%v' expected '%v'", resp.String(), "DOGE_1234567890") } } + +func TestFetchSpotExchangeLimits(t *testing.T) { + t.Parallel() + limits, err := b.FetchSpotExchangeLimits(context.Background()) + if !errors.Is(err, nil) { + t.Errorf("received '%v', epected '%v'", err, nil) + } + if len(limits) == 0 { + t.Error("expected a response") + } +} diff --git a/exchanges/binance/binance_types.go b/exchanges/binance/binance_types.go index 8878ab763e3..13d9e81d3ad 100644 --- a/exchanges/binance/binance_types.go +++ b/exchanges/binance/binance_types.go @@ -4,6 +4,7 @@ import ( "sync" "time" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) @@ -426,9 +427,9 @@ type QueryOrderData struct { // Balance holds query order data type Balance struct { - Asset string `json:"asset"` - Free string `json:"free"` - Locked string `json:"locked"` + Asset string `json:"asset"` + Free decimal.Decimal `json:"free"` + Locked decimal.Decimal `json:"locked"` } // Account holds the account data diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index e4f089d9821..27e0062ca9f 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -186,9 +186,12 @@ func (b *Binance) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: spotAPIURL, @@ -489,7 +492,7 @@ func (b *Binance) UpdateTickers(ctx context.Context, a asset.Item) error { } } case asset.USDTMarginedFutures: - tick, err := b.U24HTickerPriceChangeStats(ctx, currency.Pair{}) + tick, err := b.U24HTickerPriceChangeStats(ctx, currency.EMPTYPAIR) if err != nil { return err } @@ -516,7 +519,7 @@ func (b *Binance) UpdateTickers(ctx context.Context, a asset.Item) error { } } case asset.CoinMarginedFutures: - tick, err := b.GetFuturesSwapTickerChangeStats(ctx, currency.Pair{}, "") + tick, err := b.GetFuturesSwapTickerChangeStats(ctx, currency.EMPTYPAIR, "") if err != nil { return err } @@ -702,20 +705,14 @@ func (b *Binance) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( var currencyBalance []account.Balance for i := range raw.Balances { - freeCurrency, parseErr := strconv.ParseFloat(raw.Balances[i].Free, 64) - if parseErr != nil { - return info, parseErr - } - - lockedCurrency, parseErr := strconv.ParseFloat(raw.Balances[i].Locked, 64) - if parseErr != nil { - return info, parseErr - } + free := raw.Balances[i].Free.InexactFloat64() + locked := raw.Balances[i].Locked.InexactFloat64() currencyBalance = append(currencyBalance, account.Balance{ CurrencyName: currency.NewCode(raw.Balances[i].Asset), - TotalValue: freeCurrency + lockedCurrency, - Hold: lockedCurrency, + Total: free + locked, + Hold: locked, + Free: free, }) } @@ -730,8 +727,9 @@ func (b *Binance) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( for i := range accData.Assets { currencyDetails = append(currencyDetails, account.Balance{ CurrencyName: currency.NewCode(accData.Assets[i].Asset), - TotalValue: accData.Assets[i].WalletBalance, - Hold: accData.Assets[i].WalletBalance - accData.Assets[i].MarginBalance, + Total: accData.Assets[i].WalletBalance, + Hold: accData.Assets[i].WalletBalance - accData.Assets[i].AvailableBalance, + Free: accData.Assets[i].AvailableBalance, }) } @@ -742,16 +740,22 @@ func (b *Binance) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( if err != nil { return info, err } - var currencyDetails []account.Balance + accountCurrencyDetails := make(map[string][]account.Balance) for i := range accData { - currencyDetails = append(currencyDetails, account.Balance{ - CurrencyName: currency.NewCode(accData[i].Asset), - TotalValue: accData[i].Balance, - Hold: accData[i].Balance - accData[i].AvailableBalance, - }) + currencyDetails := accountCurrencyDetails[accData[i].AccountAlias] + accountCurrencyDetails[accData[i].AccountAlias] = append( + currencyDetails, account.Balance{ + CurrencyName: currency.NewCode(accData[i].Asset), + Total: accData[i].Balance, + Hold: accData[i].Balance - accData[i].AvailableBalance, + Free: accData[i].AvailableBalance, + }, + ) } - acc.Currencies = currencyDetails + if info.Accounts, err = account.CollectBalances(accountCurrencyDetails, assetType); err != nil { + return account.Holdings{}, err + } case asset.Margin: accData, err := b.GetMarginAccount(ctx) if err != nil { @@ -760,9 +764,12 @@ func (b *Binance) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( var currencyDetails []account.Balance for i := range accData.UserAssets { currencyDetails = append(currencyDetails, account.Balance{ - CurrencyName: currency.NewCode(accData.UserAssets[i].Asset), - TotalValue: accData.UserAssets[i].Free + accData.UserAssets[i].Locked, - Hold: accData.UserAssets[i].Locked, + CurrencyName: currency.NewCode(accData.UserAssets[i].Asset), + Total: accData.UserAssets[i].Free + accData.UserAssets[i].Locked, + Hold: accData.UserAssets[i].Locked, + Free: accData.UserAssets[i].Free, + AvailableWithoutBorrow: accData.UserAssets[i].Free - accData.UserAssets[i].Borrowed, + Borrowed: accData.UserAssets[i].Borrowed, }) } @@ -1323,7 +1330,7 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque } if len(req.Pairs) == 0 || len(req.Pairs) >= 40 { // sending an empty currency pair retrieves data for all currencies - req.Pairs = append(req.Pairs, currency.Pair{}) + req.Pairs = append(req.Pairs, currency.EMPTYPAIR) } var orders []order.Detail for i := range req.Pairs { @@ -1832,7 +1839,7 @@ func (b *Binance) GetAvailableTransferChains(ctx context.Context, cryptocurrency func (b *Binance) FormatExchangeCurrency(p currency.Pair, a asset.Item) (currency.Pair, error) { pairFmt, err := b.GetPairFormat(a, true) if err != nil { - return currency.Pair{}, err + return currency.EMPTYPAIR, err } if a == asset.USDTMarginedFutures { return b.formatUSDTMarginedFuturesPair(p, pairFmt), nil diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 1424d6e80ea..8983f3c7df6 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -165,9 +165,12 @@ func (b *Bitfinex) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: bitfinexAPIURLBase, @@ -500,8 +503,9 @@ func (b *Bitfinex) UpdateAccountInfo(ctx context.Context, assetType asset.Item) Accounts[i].Currencies = append(Accounts[i].Currencies, account.Balance{ CurrencyName: currency.NewCode(accountBalance[x].Currency), - TotalValue: accountBalance[x].Amount, + Total: accountBalance[x].Amount, Hold: accountBalance[x].Amount - accountBalance[x].Available, + Free: accountBalance[x].Available, }) } } diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index 8b0bb911cf7..5007fe14886 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -94,9 +94,12 @@ func (b *Bitflyer) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: japanURL, diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 1a5223d2c54..aca5bd1a849 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -129,9 +129,12 @@ func (b *Bithumb) SetDefaults() { }, }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: apiURL, @@ -373,10 +376,16 @@ func (b *Bithumb) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( key) } + avail, ok := bal.Available[key] + if !ok { + avail = totalAmount - hold + } + exchangeBalances = append(exchangeBalances, account.Balance{ CurrencyName: currency.NewCode(key), - TotalValue: totalAmount, + Total: totalAmount, Hold: hold, + Free: avail, }) } diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index 2ba2bff5c25..302d0ed8c83 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -1148,7 +1148,8 @@ func TestUpdateTicker(t *testing.T) { } func TestUpdateTickers(t *testing.T) { - err := b.UpdateTickers(context.Background(), asset.Spot) + t.Parallel() + err := b.UpdateTickers(context.Background(), asset.PerpetualContract) if err != nil { t.Fatal(err) } diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 90e20ca9705..f2f3675375d 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -124,9 +124,12 @@ func (b *Bitmex) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: bitmexAPIURL, @@ -423,28 +426,28 @@ func (b *Bitmex) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a return info, err } - var accountID string - var balances []account.Balance + accountBalances := make(map[string][]account.Balance) // Need to update to add Margin/Liquidity availability for i := range userMargins { - accountID = strconv.FormatInt(userMargins[i].Account, 10) + accountID := strconv.FormatInt(userMargins[i].Account, 10) - wallet, err := b.GetWalletInfo(ctx, userMargins[i].Currency) + var wallet WalletInfo + wallet, err = b.GetWalletInfo(ctx, userMargins[i].Currency) if err != nil { continue } - balances = append(balances, account.Balance{ - CurrencyName: currency.NewCode(wallet.Currency), - TotalValue: wallet.Amount, - }) + accountBalances[accountID] = append( + accountBalances[accountID], account.Balance{ + CurrencyName: currency.NewCode(wallet.Currency), + Total: wallet.Amount, + }, + ) } - info.Accounts = append(info.Accounts, - account.SubAccount{ - ID: accountID, - Currencies: balances, - }) + if info.Accounts, err = account.CollectBalances(accountBalances, assetType); err != nil { + return account.Holdings{}, err + } info.Exchange = b.Name if err := account.Process(&info); err != nil { diff --git a/exchanges/bitstamp/bitstamp_mock_test.go b/exchanges/bitstamp/bitstamp_mock_test.go index 29c1cc580c0..5d0755c7d75 100644 --- a/exchanges/bitstamp/bitstamp_mock_test.go +++ b/exchanges/bitstamp/bitstamp_mock_test.go @@ -46,7 +46,10 @@ func TestMain(m *testing.M) { log.Fatalf("Mock server error %s", err) } - b.HTTPClient = newClient + err = b.SetHTTPClient(newClient) + if err != nil { + log.Fatalf("Mock server error %s", err) + } endpointMap := b.API.Endpoints.GetURLMap() for k := range endpointMap { err = b.API.Endpoints.SetRunning(k, serverDetails+"/api") diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index c502acf51bf..17e40a6fb29 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -129,9 +129,12 @@ func (b *Bitstamp) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(bitstampRateInterval, bitstampRequestRate))) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: bitstampAPIURL, @@ -437,8 +440,9 @@ func (b *Bitstamp) UpdateAccountInfo(ctx context.Context, assetType asset.Item) for k, v := range accountBalance { currencies = append(currencies, account.Balance{ CurrencyName: currency.NewCode(k), - TotalValue: v.Available, + Total: v.Balance, Hold: v.Reserved, + Free: v.Available, }) } response.Accounts = append(response.Accounts, account.SubAccount{ diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index b9292930ca6..6bda4f46cec 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -120,9 +120,12 @@ func (b *Bittrex) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(ratePeriod, rateLimit))) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() @@ -413,8 +416,9 @@ func (b *Bittrex) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( for i := range balanceData { currencies = append(currencies, account.Balance{ CurrencyName: currency.NewCode(balanceData[i].CurrencySymbol), - TotalValue: balanceData[i].Total, + Total: balanceData[i].Total, Hold: balanceData[i].Total - balanceData[i].Available, + Free: balanceData[i].Available, }) } diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 816083f70f2..c6d3652b2a3 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -119,9 +119,12 @@ func (b *BTCMarkets) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: btcMarketsAPIURL, @@ -419,8 +422,10 @@ func (b *BTCMarkets) UpdateAccountInfo(ctx context.Context, assetType asset.Item total := data[key].Balance acc.Currencies = append(acc.Currencies, account.Balance{CurrencyName: c, - TotalValue: total, - Hold: hold}) + Total: total, + Hold: hold, + Free: total - hold, + }) } resp.Accounts = append(resp.Accounts, acc) resp.Exchange = b.Name diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 9c59178b19f..de266ed8fab 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -149,9 +149,12 @@ func (b *BTSE) SetDefaults() { }, } - b.Requester = request.New(b.Name, + b.Requester, err = request.New(b.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.API.Endpoints = b.NewEndpoints() err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: btseAPIURL, @@ -397,8 +400,9 @@ func (b *BTSE) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (acc currencies = append(currencies, account.Balance{ CurrencyName: currency.NewCode(balance[b].Currency), - TotalValue: balance[b].Total, + Total: balance[b].Total, Hold: balance[b].Total - balance[b].Available, + Free: balance[b].Available, }, ) } diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index c53071dc1d8..5bc2458fe4f 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -810,7 +810,7 @@ func (c *CoinbasePro) calculateTradingFee(trailingVolume []Volume, base, quote c func getInternationalBankWithdrawalFee(c currency.Code) float64 { var fee float64 - if c.Match(currency.USD) { + if c.Equal(currency.USD) { fee = 25 } else if c == currency.EUR { fee = 0.15 @@ -822,7 +822,7 @@ func getInternationalBankWithdrawalFee(c currency.Code) float64 { func getInternationalBankDepositFee(c currency.Code) float64 { var fee float64 - if c.Match(currency.USD) { + if c.Equal(currency.USD) { fee = 10 } else if c == currency.EUR { fee = 0.15 diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index c1a007b89e8..4db5865fcc1 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -130,9 +130,12 @@ func (c *CoinbasePro) SetDefaults() { }, } - c.Requester = request.New(c.Name, + c.Requester, err = request.New(c.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } c.API.Endpoints = c.NewEndpoints() err = c.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: coinbaseproAPIURL, @@ -325,19 +328,23 @@ func (c *CoinbasePro) UpdateAccountInfo(ctx context.Context, assetType asset.Ite return response, err } - var currencies []account.Balance + accountCurrencies := make(map[string][]account.Balance) for i := range accountBalance { - var exchangeCurrency account.Balance - exchangeCurrency.CurrencyName = currency.NewCode(accountBalance[i].Currency) - exchangeCurrency.TotalValue = accountBalance[i].Available - exchangeCurrency.Hold = accountBalance[i].Hold - - currencies = append(currencies, exchangeCurrency) + profileID := accountBalance[i].ProfileID + currencies := accountCurrencies[profileID] + accountCurrencies[profileID] = append(currencies, account.Balance{ + CurrencyName: currency.NewCode(accountBalance[i].Currency), + Total: accountBalance[i].Balance, + Hold: accountBalance[i].Hold, + Free: accountBalance[i].Available, + AvailableWithoutBorrow: accountBalance[i].Available - accountBalance[i].FundedAmount, + Borrowed: accountBalance[i].FundedAmount, + }) } - response.Accounts = append(response.Accounts, account.SubAccount{ - Currencies: currencies, - }) + if response.Accounts, err = account.CollectBalances(accountCurrencies, assetType); err != nil { + return account.Holdings{}, err + } err = account.Process(&response) if err != nil { diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 4b278fcce03..b12f7fa2250 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -403,7 +403,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, amount float64) float64 func getInternationalBankDepositFee(c currency.Code, amount float64) float64 { var fee float64 - if c.Match(currency.USD) { + if c.Equal(currency.USD) { if amount*0.001 < 10 { fee = 10 } else { diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 1ba6771e7af..aefc5f8d680 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -113,8 +113,11 @@ func (c *COINUT) SetDefaults() { }, } - c.Requester = request.New(c.Name, + c.Requester, err = request.New(c.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } c.API.Endpoints = c.NewEndpoints() err = c.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: coinutAPIURL, @@ -328,59 +331,59 @@ func (c *COINUT) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a var balances = []account.Balance{ { CurrencyName: currency.BCH, - TotalValue: bal.BCH, + Total: bal.BCH, }, { CurrencyName: currency.BTC, - TotalValue: bal.BTC, + Total: bal.BTC, }, { CurrencyName: currency.BTG, - TotalValue: bal.BTG, + Total: bal.BTG, }, { CurrencyName: currency.CAD, - TotalValue: bal.CAD, + Total: bal.CAD, }, { CurrencyName: currency.ETC, - TotalValue: bal.ETC, + Total: bal.ETC, }, { CurrencyName: currency.ETH, - TotalValue: bal.ETH, + Total: bal.ETH, }, { CurrencyName: currency.LCH, - TotalValue: bal.LCH, + Total: bal.LCH, }, { CurrencyName: currency.LTC, - TotalValue: bal.LTC, + Total: bal.LTC, }, { CurrencyName: currency.MYR, - TotalValue: bal.MYR, + Total: bal.MYR, }, { CurrencyName: currency.SGD, - TotalValue: bal.SGD, + Total: bal.SGD, }, { CurrencyName: currency.USD, - TotalValue: bal.USD, + Total: bal.USD, }, { CurrencyName: currency.USDT, - TotalValue: bal.USDT, + Total: bal.USDT, }, { CurrencyName: currency.XMR, - TotalValue: bal.XMR, + Total: bal.XMR, }, { CurrencyName: currency.ZEC, - TotalValue: bal.ZEC, + Total: bal.ZEC, }, } info.Exchange = c.Name diff --git a/exchanges/currencystate/currency_state_test.go b/exchanges/currencystate/currency_state_test.go index de008c538af..b8a2ef8037a 100644 --- a/exchanges/currencystate/currency_state_test.go +++ b/exchanges/currencystate/currency_state_test.go @@ -43,12 +43,12 @@ func TestGetSnapshot(t *testing.T) { func TestCanTradePair(t *testing.T) { t.Parallel() - err := (*States)(nil).CanTradePair(currency.Pair{}, "") + err := (*States)(nil).CanTradePair(currency.EMPTYPAIR, "") if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanTradePair(currency.Pair{}, "") + err = (&States{}).CanTradePair(currency.EMPTYPAIR, "") if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -115,11 +115,11 @@ func TestCanTradePair(t *testing.T) { func TestStatesCanTrade(t *testing.T) { t.Parallel() - err := (*States)(nil).CanTrade(currency.Code{}, "") + err := (*States)(nil).CanTrade(currency.EMPTYCODE, "") if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanTrade(currency.Code{}, "") + err = (&States{}).CanTrade(currency.EMPTYCODE, "") if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -127,11 +127,11 @@ func TestStatesCanTrade(t *testing.T) { func TestStatesCanWithdraw(t *testing.T) { t.Parallel() - err := (*States)(nil).CanWithdraw(currency.Code{}, "") + err := (*States)(nil).CanWithdraw(currency.EMPTYCODE, "") if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanWithdraw(currency.Code{}, "") + err = (&States{}).CanWithdraw(currency.EMPTYCODE, "") if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -161,11 +161,11 @@ func TestStatesCanWithdraw(t *testing.T) { func TestStatesCanDeposit(t *testing.T) { t.Parallel() - err := (*States)(nil).CanDeposit(currency.Code{}, "") + err := (*States)(nil).CanDeposit(currency.EMPTYCODE, "") if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).CanDeposit(currency.Code{}, "") + err = (&States{}).CanDeposit(currency.EMPTYCODE, "") if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -246,12 +246,12 @@ func TestStatesUpdateAll(t *testing.T) { func TestStatesUpdate(t *testing.T) { t.Parallel() - err := (*States)(nil).Update(currency.Code{}, "", Options{}) + err := (*States)(nil).Update(currency.EMPTYCODE, "", Options{}) if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - err = (&States{}).Update(currency.Code{}, "", Options{}) + err = (&States{}).Update(currency.EMPTYCODE, "", Options{}) if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } @@ -273,12 +273,12 @@ func TestStatesUpdate(t *testing.T) { func TestStatesGet(t *testing.T) { t.Parallel() - _, err := (*States)(nil).Get(currency.Code{}, "") + _, err := (*States)(nil).Get(currency.EMPTYCODE, "") if !errors.Is(err, errNilStates) { t.Fatalf("received: %v, but expected: %v", err, errNilStates) } - _, err = (&States{}).Get(currency.Code{}, "") + _, err = (&States{}).Get(currency.EMPTYCODE, "") if !errors.Is(err, errEmptyCurrency) { t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 9492052e40d..faa54ff2307 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -5,13 +5,11 @@ import ( "errors" "fmt" "net" - "net/http" "net/url" "strconv" "strings" "time" - "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" @@ -22,7 +20,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" - "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/trade" "github.com/thrasher-corp/gocryptotrader/log" @@ -48,54 +45,8 @@ var ( ErrAuthenticatedRequestWithoutCredentialsSet = errors.New("authenticated HTTP request called but not supported due to unset/default API keys") errEndpointStringNotFound = errors.New("endpoint string not found") - errTransportNotSet = errors.New("transport not set, cannot set timeout") - - // ErrPairNotFound is an error message for when unable to find a currency pair - ErrPairNotFound = errors.New("pair not found") ) -func (b *Base) checkAndInitRequester() { - b.Requester = request.New(b.Name, - &http.Client{Transport: new(http.Transport)}) -} - -// SetHTTPClientTimeout sets the timeout value for the exchanges HTTP Client and -// also the underlying transports idle connection timeout -func (b *Base) SetHTTPClientTimeout(t time.Duration) error { - b.checkAndInitRequester() - b.Requester.HTTPClient.Timeout = t - tr, ok := b.Requester.HTTPClient.Transport.(*http.Transport) - if !ok { - return errTransportNotSet - } - tr.IdleConnTimeout = t - return nil -} - -// SetHTTPClient sets exchanges HTTP client -func (b *Base) SetHTTPClient(h *http.Client) { - b.checkAndInitRequester() - b.Requester.HTTPClient = h -} - -// GetHTTPClient gets the exchanges HTTP client -func (b *Base) GetHTTPClient() *http.Client { - b.checkAndInitRequester() - return b.Requester.HTTPClient -} - -// SetHTTPClientUserAgent sets the exchanges HTTP user agent -func (b *Base) SetHTTPClientUserAgent(ua string) { - b.checkAndInitRequester() - b.Requester.UserAgent = ua - b.HTTPUserAgent = ua -} - -// GetHTTPClientUserAgent gets the exchanges HTTP user agent -func (b *Base) GetHTTPClientUserAgent() string { - return b.HTTPUserAgent -} - // SetClientProxyAddress sets a proxy address for REST and websocket requests func (b *Base) SetClientProxyAddress(addr string) error { if addr == "" { @@ -441,17 +392,16 @@ func (b *Base) GetEnabledPairs(a asset.Item) (currency.Pairs, error) { // GetRequestFormattedPairAndAssetType is a method that returns the enabled currency pair of // along with its asset type. Only use when there is no chance of the same name crossing over func (b *Base) GetRequestFormattedPairAndAssetType(p string) (currency.Pair, asset.Item, error) { - assetTypes := b.GetAssetTypes(false) - var response currency.Pair + assetTypes := b.GetAssetTypes(true) for i := range assetTypes { format, err := b.GetPairFormat(assetTypes[i], true) if err != nil { - return response, assetTypes[i], err + return currency.EMPTYPAIR, assetTypes[i], err } pairs, err := b.CurrencyPairs.GetPairs(assetTypes[i], true) if err != nil { - return response, assetTypes[i], err + return currency.EMPTYPAIR, assetTypes[i], err } for j := range pairs { @@ -461,8 +411,7 @@ func (b *Base) GetRequestFormattedPairAndAssetType(p string) (currency.Pair, ass } } } - return response, "", - fmt.Errorf("%s %w", p, ErrPairNotFound) + return currency.EMPTYPAIR, "", fmt.Errorf("%s %w", p, currency.ErrPairNotFound) } // GetAvailablePairs is a method that returns the available currency pairs @@ -535,7 +484,7 @@ func (b *Base) FormatExchangeCurrencies(pairs []currency.Pair, assetType asset.I func (b *Base) FormatExchangeCurrency(p currency.Pair, assetType asset.Item) (currency.Pair, error) { pairFmt, err := b.GetPairFormat(assetType, true) if err != nil { - return currency.Pair{}, err + return currency.EMPTYPAIR, err } return p.Format(pairFmt.Delimiter, pairFmt.Uppercase), nil } @@ -607,7 +556,10 @@ func (b *Base) SetupDefaults(exch *config.Exchange) error { b.HTTPDebugging = exch.HTTPDebugging b.BypassConfigFormatUpgrades = exch.CurrencyPairs.BypassConfigFormatUpgrades - b.SetHTTPClientUserAgent(exch.HTTPUserAgent) + err = b.SetHTTPClientUserAgent(exch.HTTPUserAgent) + if err != nil { + return err + } b.SetCurrencyPairFormat() err = b.SetConfigPairs() @@ -1471,13 +1423,13 @@ func (b *Base) CalculatePNL(context.Context, *order.PNLCalculatorRequest) (*orde // ScaleCollateral is an overridable function to determine how much // collateral is usable in futures positions -func (b *Base) ScaleCollateral(context.Context, string, *order.CollateralCalculator) (decimal.Decimal, error) { - return decimal.Zero, common.ErrNotYetImplemented +func (b *Base) ScaleCollateral(context.Context, string, *order.CollateralCalculator) (*order.CollateralByCurrency, error) { + return nil, common.ErrNotYetImplemented } // CalculateTotalCollateral takes in n collateral calculators to determine an overall // standing in a singular currency. See FTX's implementation -func (b *Base) CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, calculators []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { +func (b *Base) CalculateTotalCollateral(ctx context.Context, calculator *order.TotalCollateralCalculator) (*order.TotalCollateralResponse, error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index e0cfb779549..38d8f6fc0f8 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -5,9 +5,7 @@ import ( "errors" "fmt" "net" - "net/http" "os" - "strings" "testing" "time" @@ -193,76 +191,21 @@ func TestSetDefaultEndpoints(t *testing.T) { } } -func TestHTTPClient(t *testing.T) { +func TestSetClientProxyAddress(t *testing.T) { t.Parallel() - r := Base{Name: "asdf"} - err := r.SetHTTPClientTimeout(time.Second * 5) - if err != nil { - t.Fatal(err) - } - - if r.GetHTTPClient().Timeout != time.Second*5 { - t.Fatalf("TestHTTPClient unexpected value, received %v", r.GetHTTPClient().Timeout) - } - - r.Requester = nil - newClient := new(http.Client) - newClient.Timeout = time.Second * 10 - - r.SetHTTPClient(newClient) - if r.GetHTTPClient().Timeout != time.Second*10 { - t.Fatalf("TestHTTPClient unexpected value") - } - - r.Requester = nil - if r.GetHTTPClient() == nil { - t.Fatalf("TestHTTPClient unexpected value") - } - - b := Base{Name: "RAWR"} - - b.Requester = request.New(b.Name, new(http.Client)) - err = b.SetHTTPClientTimeout(time.Second * 5) - if !errors.Is(err, errTransportNotSet) { - t.Fatalf("received: %v but expected: %v", err, errTransportNotSet) - } - b.Requester = request.New(b.Name, &http.Client{Transport: new(http.Transport)}) - err = b.SetHTTPClientTimeout(time.Second * 5) + requester, err := request.New("rawr", + common.NewHTTPClientWithTimeout(time.Second*15)) if err != nil { t.Fatal(err) } - if b.GetHTTPClient().Timeout != time.Second*5 { - t.Fatalf("TestHTTPClient unexpected value") - } - - newClient = new(http.Client) - newClient.Timeout = time.Second * 10 - - b.SetHTTPClient(newClient) - if b.GetHTTPClient().Timeout != time.Second*10 { - t.Fatalf("TestHTTPClient unexpected value") - } - - b.SetHTTPClientUserAgent("epicUserAgent") - if !strings.Contains(b.GetHTTPClientUserAgent(), "epicUserAgent") { - t.Error("user agent not set properly") - } -} - -func TestSetClientProxyAddress(t *testing.T) { - t.Parallel() - - requester := request.New("rawr", - common.NewHTTPClientWithTimeout(time.Second*15)) - newBase := Base{ Name: "rawr", Requester: requester} newBase.Websocket = stream.New() - err := newBase.SetClientProxyAddress("") + err = newBase.SetClientProxyAddress("") if err != nil { t.Error(err) } @@ -289,13 +232,6 @@ func TestSetClientProxyAddress(t *testing.T) { if newBase.Websocket.GetProxyAddress() != "http://www.valid.com" { t.Error("SetClientProxyAddress error", err) } - - // Nil out transport - newBase.Requester.HTTPClient.Transport = nil - err = newBase.SetClientProxyAddress("http://www.valid.com") - if err == nil { - t.Error("error cannot be nil") - } } func TestSetFeatureDefaults(t *testing.T) { @@ -694,7 +630,7 @@ func TestLoadConfigPairs(t *testing.T) { } p = pairs[2].Format(pFmt.Delimiter, pFmt.Uppercase).String() if p != "xrp/usd" { - t.Error("incorrect value, expected xrp/usd") + t.Error("incorrect value, expected xrp/usd", p) } avail, err = b.GetAvailablePairs(asset.Spot) @@ -708,7 +644,7 @@ func TestLoadConfigPairs(t *testing.T) { } p = format.String() if p != "xrp~usd" { - t.Error("incorrect value, expected xrp~usd") + t.Error("incorrect value, expected xrp~usd", p) } ps, err := b.Config.CurrencyPairs.Get(asset.Spot) if err != nil { @@ -1268,7 +1204,16 @@ func TestSetAPIKeys(t *testing.T) { func TestSetupDefaults(t *testing.T) { t.Parallel() - var b = Base{Name: "awesomeTest"} + newRequester, err := request.New("testSetupDefaults", + common.NewHTTPClientWithTimeout(0)) + if err != nil { + t.Fatal(err) + } + + var b = Base{ + Name: "awesomeTest", + Requester: newRequester, + } cfg := config.Exchange{ HTTPTimeout: time.Duration(-1), API: config.APIConfig{ @@ -1276,7 +1221,7 @@ func TestSetupDefaults(t *testing.T) { }, } - err := b.SetupDefaults(&cfg) + err = b.SetupDefaults(&cfg) if err != nil { t.Fatal(err) } @@ -1609,7 +1554,7 @@ func TestUpdatePairs(t *testing.T) { t.Fatal(err) } pairs := currency.Pairs{ - currency.Pair{}, + currency.EMPTYPAIR, p, } err = UAC.UpdatePairs(pairs, asset.Spot, true, true) @@ -2055,25 +2000,34 @@ func TestCheckTransientError(t *testing.T) { func TestDisableEnableRateLimiter(t *testing.T) { b := Base{} - b.checkAndInitRequester() err := b.EnableRateLimiter() - if err == nil { - t.Fatal("error cannot be nil") + if !errors.Is(err, request.ErrRequestSystemIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, request.ErrRequestSystemIsNil) } - err = b.DisableRateLimiter() + b.Requester, err = request.New("testingRateLimiter", common.NewHTTPClientWithTimeout(0)) if err != nil { t.Fatal(err) } err = b.DisableRateLimiter() - if err == nil { - t.Fatal("error cannot be nil") + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + err = b.DisableRateLimiter() + if !errors.Is(err, request.ErrRateLimiterAlreadyDisabled) { + t.Fatalf("received: '%v' but expected: '%v'", err, request.ErrRateLimiterAlreadyDisabled) } err = b.EnableRateLimiter() - if err != nil { - t.Fatal(err) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + err = b.EnableRateLimiter() + if !errors.Is(err, request.ErrRateLimiterAlreadyEnabled) { + t.Fatalf("received: '%v' but expected: '%v'", err, request.ErrRateLimiterAlreadyEnabled) } } @@ -2484,7 +2438,7 @@ func TestScaleCollateral(t *testing.T) { func TestCalculateTotalCollateral(t *testing.T) { t.Parallel() var b Base - if _, err := b.CalculateTotalCollateral(context.Background(), "", false, nil); !errors.Is(err, common.ErrNotYetImplemented) { + if _, err := b.CalculateTotalCollateral(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) { t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented) } } diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 4f35119ebad..887df7ea805 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -219,7 +219,6 @@ type Base struct { CurrencyPairs currency.PairsManager Features Features HTTPTimeout time.Duration - HTTPUserAgent string HTTPRecording bool HTTPDebugging bool BypassConfigFormatUpgrades bool diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 89e5e99901a..0cce9b52635 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -110,9 +110,12 @@ func (e *EXMO) SetDefaults() { }, } - e.Requester = request.New(e.Name, + e.Requester, err = request.New(e.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(exmoRateInterval, exmoRequestRate))) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } e.API.Endpoints = e.NewEndpoints() err = e.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: exmoAPIURL, @@ -354,12 +357,21 @@ func (e *EXMO) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (acc var exchangeCurrency account.Balance exchangeCurrency.CurrencyName = currency.NewCode(x) for z, w := range result.Reserved { - if z == x { - avail, _ := strconv.ParseFloat(y, 64) - reserved, _ := strconv.ParseFloat(w, 64) - exchangeCurrency.TotalValue = avail + reserved - exchangeCurrency.Hold = reserved + if z != x { + continue + } + var avail, reserved float64 + avail, err = strconv.ParseFloat(y, 64) + if err != nil { + return response, err + } + reserved, err = strconv.ParseFloat(w, 64) + if err != nil { + return response, err } + exchangeCurrency.Total = avail + reserved + exchangeCurrency.Hold = reserved + exchangeCurrency.Free = avail } currencies = append(currencies, exchangeCurrency) } diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 6573d763273..18ae72dfd48 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -10,6 +10,7 @@ import ( "math" "net/http" "net/url" + "sort" "strconv" "strings" "time" @@ -57,6 +58,7 @@ const ( getDepositHistory = "/wallet/deposits" getWithdrawalHistory = "/wallet/withdrawals" withdrawRequest = "/wallet/withdrawals" + collateral = "/wallet/collateral" getOpenOrders = "/orders" getOrderHistory = "/orders/history" getOpenTriggerOrders = "/conditional_orders" @@ -540,11 +542,19 @@ func (f *FTX) GetCoins(ctx context.Context, subAccount string) ([]WalletCoinsDat } // GetBalances gets balances of the account -func (f *FTX) GetBalances(ctx context.Context, subAccount string) ([]WalletBalance, error) { +func (f *FTX) GetBalances(ctx context.Context, subAccount string, includeLockedBreakdown, includeFreeIgnoringCollateral bool) ([]WalletBalance, error) { resp := struct { Data []WalletBalance `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, getBalances, subAccount, nil, &resp) + vals := url.Values{} + if includeLockedBreakdown { + vals.Set("includeLockedBreakdown", strconv.FormatBool(includeLockedBreakdown)) + } + if includeFreeIgnoringCollateral { + vals.Set("includeFreeIgnoringCollateral", strconv.FormatBool(includeFreeIgnoringCollateral)) + } + balanceURL := common.EncodeURLValues(getBalances, vals) + return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, balanceURL, subAccount, nil, &resp) } // GetAllWalletBalances gets all wallets' balances @@ -868,31 +878,59 @@ func (f *FTX) DeleteTriggerOrder(ctx context.Context, orderID string) (string, e return f.deleteOrderByPath(ctx, cancelTriggerOrder+orderID) } -// GetFills gets fills' data -func (f *FTX) GetFills(ctx context.Context, market currency.Pair, item asset.Item, limit string, startTime, endTime time.Time) ([]FillsData, error) { - resp := struct { - Data []FillsData `json:"result"` - }{} - params := url.Values{} - if !market.IsEmpty() { - fp, err := f.FormatExchangeCurrency(market, item) +// GetFills gets order fills data and ensures that all +// fills are retrieved from the supplied timeframe +func (f *FTX) GetFills(ctx context.Context, market currency.Pair, item asset.Item, startTime, endTime time.Time) ([]FillsData, error) { + var resp []FillsData + var nextEnd = endTime + limit := 200 + for { + data := struct { + Data []FillsData `json:"result"` + }{} + params := url.Values{} + params.Add("limit", strconv.FormatInt(int64(limit), 10)) + if !market.IsEmpty() { + fp, err := f.FormatExchangeCurrency(market, item) + if err != nil { + return nil, err + } + params.Set("market", fp.String()) + } + if !startTime.IsZero() && !endTime.IsZero() { + if startTime.After(endTime) { + return data.Data, errStartTimeCannotBeAfterEndTime + } + params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) + params.Set("end_time", strconv.FormatInt(nextEnd.Unix(), 10)) + } + endpoint := common.EncodeURLValues(getFills, params) + err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &data) if err != nil { return nil, err } - params.Set("market", fp.String()) - } - if limit != "" { - params.Set("limit", limit) - } - if !startTime.IsZero() && !endTime.IsZero() { - if startTime.After(endTime) { - return resp.Data, errStartTimeCannotBeAfterEndTime + if len(data.Data) == 0 || + data.Data[len(data.Data)-1].Time.Equal(nextEnd) { + break } - params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) - params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) + data: + for i := range data.Data { + for j := range resp { + if resp[j].ID == data.Data[i].ID { + continue data + } + } + resp = append(resp, data.Data[i]) + } + if len(data.Data) < limit { + break + } + nextEnd = data.Data[len(data.Data)-1].Time } - endpoint := common.EncodeURLValues(getFills, params) - return resp.Data, f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, endpoint, "", nil, &resp) + sort.Slice(resp, func(i, j int) bool { + return resp[i].Time.Before(resp[j].Time) + }) + return resp, nil } // GetFundingPayments gets funding payments @@ -1506,10 +1544,29 @@ func (f *FTX) FetchExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, err return limits, nil } +// GetCollateral returns total collateral and the breakdown of +// collateral contributions +func (f *FTX) GetCollateral(ctx context.Context, maintenance bool) (*CollateralResponse, error) { + resp := struct { + Data CollateralResponse `json:"result"` + }{} + u := url.Values{} + if maintenance { + u.Add("marginType", "maintenance") + } else { + u.Add("marginType", "initial") + } + url := common.EncodeURLValues(collateral, u) + if err := f.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, url, "", nil, &resp); err != nil { + return nil, err + } + return &resp.Data, nil +} + // LoadCollateralWeightings sets the collateral weights for // currencies supported by FTX func (f *FTX) LoadCollateralWeightings(ctx context.Context) error { - f.collateralWeight = make(map[string]CollateralWeight) + f.collateralWeight = make(map[*currency.Item]CollateralWeight) // taken from https://help.ftx.com/hc/en-us/articles/360031149632-Non-USD-Collateral // sets default, then uses the latest from FTX f.collateralWeight.load("1INCH", 0.9, 0.85, 0.0005) @@ -1670,19 +1727,22 @@ func (c CollateralWeightHolder) hasData() bool { } func (c CollateralWeightHolder) loadTotal(code string, weighting float64) { - currencyCollateral := c[code] + cc := currency.NewCode(code) + currencyCollateral := c[cc.Item] currencyCollateral.Total = weighting - c[code] = currencyCollateral + c[cc.Item] = currencyCollateral } func (c CollateralWeightHolder) loadInitialMarginFraction(code string, imf float64) { - currencyCollateral := c[code] + cc := currency.NewCode(code) + currencyCollateral := c[cc.Item] currencyCollateral.InitialMarginFractionFactor = imf - c[code] = currencyCollateral + c[cc.Item] = currencyCollateral } func (c CollateralWeightHolder) load(code string, total, initial, imfFactor float64) { - c[code] = CollateralWeight{ + cc := currency.NewCode(code) + c[cc.Item] = CollateralWeight{ Total: total, Initial: initial, InitialMarginFractionFactor: imfFactor, diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index bee7fbcb6e5..9b24791ca61 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -286,7 +286,19 @@ func TestGetBalances(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetBalances(context.Background(), subaccount) + _, err := f.GetBalances(context.Background(), subaccount, false, false) + if err != nil { + t.Error(err) + } + _, err = f.GetBalances(context.Background(), subaccount, true, false) + if err != nil { + t.Error(err) + } + _, err = f.GetBalances(context.Background(), subaccount, false, true) + if err != nil { + t.Error(err) + } + _, err = f.GetBalances(context.Background(), subaccount, true, true) if err != nil { t.Error(err) } @@ -739,24 +751,28 @@ func TestGetFills(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - // optional params - _, err := f.GetFills(context.Background(), spotPair, asset.Spot, "", time.Time{}, time.Time{}) - if err != nil { - t.Error(err) + _, err := f.GetFills(context.Background(), + currency.Pair{}, asset.Futures, time.Now().Add(time.Hour*24*365), time.Now()) + if !errors.Is(err, errStartTimeCannotBeAfterEndTime) { + t.Errorf("received '%v' expected '%v'", err, errStartTimeCannotBeAfterEndTime) } - _, err = f.GetFills(context.Background(), spotPair, asset.Spot, "", time.Time{}, time.Time{}) - if err != nil { - t.Error(err) + + _, err = f.GetFills(context.Background(), + currency.Pair{}, asset.Futures, time.Time{}, time.Time{}) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) } + _, err = f.GetFills(context.Background(), - spotPair, asset.Spot, "", time.Unix(authStartTime, 0), time.Unix(authEndTime, 0)) - if err != nil { - t.Error(err) + currency.Pair{}, asset.Futures, time.Now().Add(-time.Hour*24*365), time.Now()) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) } + _, err = f.GetFills(context.Background(), - spotPair, asset.Spot, "", time.Unix(authEndTime, 0), time.Unix(authStartTime, 0)) - if err != errStartTimeCannotBeAfterEndTime { - t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) + spotPair, asset.Spot, time.Now().Add(-time.Hour*24*365), time.Now()) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) } } @@ -1745,15 +1761,15 @@ func TestScaleCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, CalculateOffline: true, - CollateralAmount: decimal.NewFromInt(100000), + FreeCollateral: decimal.NewFromInt(100000), USDPrice: decimal.NewFromFloat(1.0003), }) if err != nil { t.Error(err) } - expectedUSDValue := decimal.NewFromFloat(95028.5) - if !result.Equal(expectedUSDValue) { - t.Errorf("received %v expected %v", result, expectedUSDValue) + expectedUSDValue := decimal.NewFromFloat(97529.25) + if !result.CollateralContribution.Equal(expectedUSDValue) { + t.Errorf("received %v expected %v", result.CollateralContribution, expectedUSDValue) } if !areTestAPIKeysSet() { @@ -1768,32 +1784,30 @@ func TestScaleCollateral(t *testing.T) { t.Error(err) } localScaling := 0.0 - liquidationScaling := 0.0 providedUSDValue := 0.0 for _, v := range walletInfo { for v2 := range v { coin := v[v2].Coin - if coin == "USD" { + if coin.Equal(currency.USD) { localScaling += v[v2].Total providedUSDValue += v[v2].USDValue - liquidationScaling += v[v2].Total continue } var tick MarketData - tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) + tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin.String(), "usd", "/").String()) if err != nil { - t.Error(err) + // not all markets exist like this, skip + continue } - var scaled decimal.Decimal - scaled, err = f.ScaleCollateral( + _, err = f.ScaleCollateral( context.Background(), "", &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), + CollateralCurrency: coin, Asset: asset.Spot, Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - CollateralPrice: decimal.NewFromFloat(tick.Price), + FreeCollateral: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), CalculateOffline: true, }) if err != nil { @@ -1803,33 +1817,29 @@ func TestScaleCollateral(t *testing.T) { } t.Error(err) } - localScaling += scaled.InexactFloat64() providedUSDValue += v[v2].USDValue - - scaled, err = f.ScaleCollateral(context.Background(), + _, err = f.ScaleCollateral(context.Background(), subaccount, &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), + CollateralCurrency: coin, Asset: asset.Spot, Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - CollateralPrice: decimal.NewFromFloat(tick.Price), - IsLiquidating: true, + FreeCollateral: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), + IsForNewPosition: true, CalculateOffline: true, }) if err != nil { t.Error(err) } - liquidationScaling += scaled.InexactFloat64() - _, err = f.ScaleCollateral(context.Background(), subaccount, &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), + CollateralCurrency: coin, Asset: asset.Spot, Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - CollateralPrice: decimal.Zero, + FreeCollateral: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.Zero, IsLiquidating: true, CalculateOffline: true, }) @@ -1841,7 +1851,7 @@ func TestScaleCollateral(t *testing.T) { context.Background(), "", &order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), + CollateralCurrency: coin, Asset: asset.Spot, Side: order.Buy, }) @@ -1871,38 +1881,49 @@ func TestCalculateTotalCollateral(t *testing.T) { for _, v := range walletInfo { for v2 := range v { coin := v[v2].Coin - if coin == "USD" { + if coin.Equal(currency.USD) { total := decimal.NewFromFloat(v[v2].Total) scales = append(scales, order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), + CollateralCurrency: coin, Asset: asset.Spot, Side: order.Buy, - CollateralAmount: total, - CollateralPrice: total, + FreeCollateral: total, + USDPrice: total, CalculateOffline: true, }) continue } var tick MarketData - tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin, "usd", "/").String()) + tick, err = f.GetMarket(context.Background(), currency.NewPairWithDelimiter(coin.String(), "usd", "/").String()) if err != nil { - t.Error(err) + // some assumed markets don't exist, just don't process them + t.Log(err) + continue + } + if tick.Price == 0 { + continue } scales = append(scales, order.CollateralCalculator{ - CollateralCurrency: currency.NewCode(coin), + CollateralCurrency: coin, Asset: asset.Spot, Side: order.Buy, - CollateralAmount: decimal.NewFromFloat(v[v2].Total), - CollateralPrice: decimal.NewFromFloat(tick.Price), + FreeCollateral: decimal.NewFromFloat(v[v2].Total), + USDPrice: decimal.NewFromFloat(tick.Price), CalculateOffline: true, }) } } - total, err := f.CalculateTotalCollateral(context.Background(), subaccount, true, scales) + calc := &order.TotalCollateralCalculator{ + SubAccount: subaccount, + CollateralAssets: scales, + FetchPositions: false, + CalculateOffline: true, + } + total, err := f.CalculateTotalCollateral(context.Background(), calc) if err != nil { - t.Error(err) + t.Fatal(err) } - localScaling := total.TotalCollateral.InexactFloat64() + localScaling := total.AvailableCollateral.InexactFloat64() accountInfo, err := f.GetAccountInfo(context.Background(), subaccount) if err != nil { t.Error(err) @@ -1914,18 +1935,97 @@ func TestCalculateTotalCollateral(t *testing.T) { for i := range scales { scales[i].CalculateOffline = false } - _, err = f.CalculateTotalCollateral(context.Background(), subaccount, false, scales) + calc.CalculateOffline = false + _, err = f.CalculateTotalCollateral(context.Background(), calc) if err != nil { t.Error(err) } } +func TestCalculateTotalCollateralOnline(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test, api keys not set") + } + // nil data + _, err := f.calculateTotalCollateralOnline(context.Background(), nil, nil) + if !errors.Is(err, common.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilPointer) + } + calc := &order.TotalCollateralCalculator{} + // no currency data + _, err = f.calculateTotalCollateralOnline(context.Background(), calc, nil) + if !errors.Is(err, errCollateralCurrencyNotFound) { + t.Errorf("received '%v' expected '%v'", err, errCollateralCurrencyNotFound) + } + calc.CalculateOffline = true + calc.CollateralAssets = []order.CollateralCalculator{ + { + CollateralCurrency: currency.BTC, + }, + { + CollateralCurrency: currency.USD, + }, + } + // offline true + _, err = f.calculateTotalCollateralOnline(context.Background(), calc, nil) + if !errors.Is(err, order.ErrOfflineCalculationSet) { + t.Errorf("received '%v' expected '%v'", err, order.ErrOfflineCalculationSet) + } + + calc.CalculateOffline = false + calc.CollateralAssets[0].CalculateOffline = true + // offline true for individual currency + _, err = f.calculateTotalCollateralOnline(context.Background(), calc, nil) + if !errors.Is(err, order.ErrOfflineCalculationSet) { + t.Errorf("received '%v' expected '%v'", err, order.ErrOfflineCalculationSet) + } + // successful run + calc.CollateralAssets[0].CalculateOffline = false + result, err := f.calculateTotalCollateralOnline(context.Background(), calc, nil) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if !result.CollateralCurrency.Equal(currency.USD) { + t.Error("expected USD collateral currency") + } + curr, err := currency.NewPairFromString("BTC-PERP") + if !errors.Is(err, nil) { + t.Fatalf("received '%v' expected '%v'", err, nil) + } + // with position data + pos := []PositionData{ + { + CollateralUsed: 5, + Future: curr, + UnrealizedPNL: 10, + }, + } + _, err = f.calculateTotalCollateralOnline(context.Background(), calc, pos) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + calc.CollateralAssets = []order.CollateralCalculator{ + { + CollateralCurrency: currency.BURST, + }, + } + // irrelevant currency + result, err = f.calculateTotalCollateralOnline(context.Background(), calc, pos) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if !result.UnrealisedPNL.IsZero() { + t.Error("expected zero") + } +} + func TestCalculatePNL(t *testing.T) { t.Parallel() if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - pair := currency.NewPair(currency.BTC, currency.NewCode("1231")) + pair := currency.NewPair(currency.BTC, currency.NewCode("20211231")) positions, err := f.GetFuturesPositions(context.Background(), asset.Futures, pair, time.Date(2021, 1, 6, 4, 28, 0, 0, time.UTC), time.Date(2021, 12, 31, 4, 32, 0, 0, time.UTC)) if err != nil { t.Error(err) @@ -1976,7 +2076,7 @@ func TestGetFuturesPositions(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("skipping test, api keys not set") } - cp := currency.NewPair(currency.BTC, currency.NewCode("1231")) + cp := currency.NewPair(currency.BTC, currency.NewCode("20211231")) start := time.Now().Add(-time.Hour * 24 * 365) end := time.Now() a := asset.Futures @@ -2014,12 +2114,12 @@ func TestLoadCollateralWeightings(t *testing.T) { func TestLoadTotalIMF(t *testing.T) { t.Parallel() c := CollateralWeightHolder{} - c.loadTotal("cw", 1) - if _, ok := c["cw"]; !ok { + c.loadTotal("BTC", 1) + if _, ok := c[currency.BTC.Item]; !ok { t.Error("expected entry") } - c.loadInitialMarginFraction("cw", 1) - cw, ok := c["cw"] + c.loadInitialMarginFraction("btc", 1) + cw, ok := c[currency.BTC.Item] if !ok { t.Error("expected entry") } @@ -2034,19 +2134,19 @@ func TestLoadTotalIMF(t *testing.T) { func TestLoadCollateralWeight(t *testing.T) { t.Parallel() c := CollateralWeightHolder{} - c.load("test", 1, 2, 3) - cw, ok := c["test"] + c.load("DOGE", 1, 2, 3) + cw, ok := c[currency.DOGE.Item] if !ok { t.Fatal("expected loaded collateral weight") } if cw.Total != 1 { - t.Errorf("expected '1', received '%v'", cw.InitialMarginFractionFactor) + t.Errorf("expected '1', received '%v'", cw.Total) } if cw.Initial != 2 { - t.Errorf("expected '2', received '%v'", cw.Total) + t.Errorf("expected '2', received '%v'", cw.Initial) } if cw.InitialMarginFractionFactor != 3 { - t.Errorf("expected '3', received '%v'", cw.Total) + t.Errorf("expected '3', received '%v'", cw.InitialMarginFractionFactor) } } @@ -2077,3 +2177,18 @@ func TestGetExpiredFuture(t *testing.T) { t.Error(err) } } + +func TestGetCollateral(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip() + } + _, err := f.GetCollateral(context.Background(), false) + if err != nil { + t.Error(err) + } + _, err = f.GetCollateral(context.Background(), true) + if err != nil { + t.Error(err) + } +} diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index c3963f1112f..dade9dd49c7 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -3,6 +3,8 @@ package ftx import ( "time" + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -184,26 +186,26 @@ type IndexWeights struct { // PositionData stores data of an open position type PositionData struct { - CollateralUsed float64 `json:"collateralUsed"` - Cost float64 `json:"cost"` - CumulativeBuySize float64 `json:"cumulativeBuySize"` - CumulativeSellSize float64 `json:"cumulativeSellSize"` - EntryPrice float64 `json:"entryPrice"` - EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"` - Future string `json:"future"` - InitialMarginRequirement float64 `json:"initialMarginRequirement"` - LongOrderSize float64 `json:"longOrderSize"` - MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"` - NetSize float64 `json:"netSize"` - OpenSize float64 `json:"openSize"` - RealizedPNL float64 `json:"realizedPnl"` - RecentAverageOpenPrice float64 `json:"recentAverageOpenPrice"` - RecentBreakEvenPrice float64 `json:"recentBreakEvenPrice"` - RecentPnl float64 `json:"recentPnl"` - ShortOrderSize float64 `json:"shortOrderSize"` - Side string `json:"side"` - Size float64 `json:"size"` - UnrealizedPNL float64 `json:"unrealizedPnl"` + CollateralUsed float64 `json:"collateralUsed"` + Cost float64 `json:"cost"` + CumulativeBuySize float64 `json:"cumulativeBuySize"` + CumulativeSellSize float64 `json:"cumulativeSellSize"` + EntryPrice float64 `json:"entryPrice"` + EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"` + Future currency.Pair `json:"future"` + InitialMarginRequirement float64 `json:"initialMarginRequirement"` + LongOrderSize float64 `json:"longOrderSize"` + MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"` + NetSize float64 `json:"netSize"` + OpenSize float64 `json:"openSize"` + RealizedPNL float64 `json:"realizedPnl"` + RecentAverageOpenPrice float64 `json:"recentAverageOpenPrice"` + RecentBreakEvenPrice float64 `json:"recentBreakEvenPrice"` + RecentPnl float64 `json:"recentPnl"` + ShortOrderSize float64 `json:"shortOrderSize"` + Side string `json:"side"` + Size float64 `json:"size"` + UnrealizedPNL float64 `json:"unrealizedPnl"` } // AccountInfoData stores account data @@ -258,12 +260,25 @@ type WalletCoinsData struct { // WalletBalance stores balances data type WalletBalance struct { - Coin string `json:"coin"` - Free float64 `json:"free"` - Total float64 `json:"total"` - AvailableWithoutBorrow float64 `json:"availableWithoutBorrow"` - USDValue float64 `json:"usdValue"` - SpotBorrow float64 `json:"spotBorrow"` + Coin currency.Code `json:"coin"` + Free float64 `json:"free"` + Total float64 `json:"total"` + AvailableWithoutBorrow float64 `json:"availableWithoutBorrow"` + USDValue float64 `json:"usdValue"` + FreeIgnoringCollateral float64 `json:"freeIgnoringCollateral"` + SpotBorrow float64 `json:"spotBorrow"` + LockedBreakdown BalanceLockedBreakdown `json:"lockedBreakdown"` +} + +// BalanceLockedBreakdown provides a breakdown of where funding is +// locked up in, helpful in tracking how much one bids on NFTs +type BalanceLockedBreakdown struct { + LockedInStakes float64 `json:"lockedInStakes"` + LockedInNFTBids float64 `json:"lockedInNftBids"` + LockedInFeeVoucher float64 `json:"lockedInFeeVoucher"` + LockedInSpotMarginFundingOffers float64 `json:"lockedInSpotMarginFundingOffers"` + LockedInSpotOrders float64 `json:"lockedInSpotOrders"` + LockedAsCollateral float64 `json:"lockedAsCollateral"` } // AllWalletBalances stores all the user's account balances @@ -887,7 +902,7 @@ type StakeReward struct { } // CollateralWeightHolder stores collateral weights over the lifecycle of the application -type CollateralWeightHolder map[string]CollateralWeight +type CollateralWeightHolder map[*currency.Item]CollateralWeight // CollateralWeight holds collateral information provided by FTX // it is used to scale collateral when the currency is not in USD @@ -896,3 +911,39 @@ type CollateralWeight struct { Total float64 InitialMarginFractionFactor float64 } + +// CollateralResponse returned from the collateral endpoint +type CollateralResponse struct { + PositiveBalances []CollateralBalance `json:"positiveBalances"` + NegativeBalances []CollateralBalance `json:"negativeBalances"` + Positions []CollateralPosition `json:"positions"` + PositiveSpotBalanceTotal decimal.Decimal `json:"positiveSpotBalanceTotal"` + CollateralFromPositiveSpotBalances decimal.Decimal `json:"collateralFromPositiveSpotBalances"` + UsedBySpotMargin decimal.Decimal `json:"usedBySpotMargin"` + UsedByFutures decimal.Decimal `json:"usedByFutures"` + CollateralAvailable decimal.Decimal `json:"collateralAvailable"` +} + +// CollateralBalance holds collateral information for a coin's balance +type CollateralBalance struct { + Coin currency.Code `json:"coin"` + PositionSize decimal.Decimal `json:"positionSize"` + OpenOrderSize decimal.Decimal `json:"openOrderSize"` + Total decimal.Decimal `json:"total"` + AvailableIgnoringCollateral decimal.Decimal `json:"availableIgnoringCollateral"` + ApproximateFairMarketValue decimal.Decimal `json:"approxFair"` + CollateralContribution decimal.Decimal `json:"collateralContribution"` + CollateralUsed decimal.Decimal `json:"collateralUsed"` + CollateralWeight decimal.Decimal `json:"collateralWeight"` +} + +// CollateralPosition holds collateral information for a market position +type CollateralPosition struct { + Future currency.Pair `json:"future"` + Size decimal.Decimal `json:"size"` + OpenOrderSize decimal.Decimal `json:"openOrderSize"` + PositionSize decimal.Decimal `json:"positionSize"` + MarkPrice decimal.Decimal `json:"markPrice"` + RequiredMargin decimal.Decimal `json:"requiredMargin"` + CollateralUsed decimal.Decimal `json:"totalCollateralUsed"` +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 199a8db83b6..7ee94b03b19 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -149,9 +149,12 @@ func (f *FTX) SetDefaults() { }, } - f.Requester = request.New(f.Name, + f.Requester, err = request.New(f.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(ratePeriod, rateLimit))) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } f.API.Endpoints = f.NewEndpoints() err = f.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: ftxAPIURL, @@ -450,7 +453,7 @@ func (f *FTX) UpdateAccountInfo(ctx context.Context, a asset.Item) (account.Hold var data AllWalletBalances if f.API.Credentials.Subaccount != "" { - balances, err := f.GetBalances(ctx, "") + balances, err := f.GetBalances(ctx, "", false, false) if err != nil { return resp, err } @@ -465,19 +468,22 @@ func (f *FTX) UpdateAccountInfo(ctx context.Context, a asset.Item) (account.Hold return resp, err } } - for subName, balances := range data { // "main" defines the main account in the sub account list var acc = account.SubAccount{ID: subName, AssetType: a} for x := range balances { - c := currency.NewCode(balances[x].Coin) // the Free field includes borrow amount with available holdings // Using AvailableWithoutBorrow allows for a more accurate picture of balance hold := balances[x].Total - balances[x].AvailableWithoutBorrow acc.Currencies = append(acc.Currencies, - account.Balance{CurrencyName: c, - TotalValue: balances[x].Total, - Hold: hold}) + account.Balance{ + CurrencyName: balances[x].Coin, + Total: balances[x].Total, + Hold: hold, + AvailableWithoutBorrow: balances[x].AvailableWithoutBorrow, + Borrowed: balances[x].SpotBorrow, + Free: balances[x].Free, + }) } resp.Accounts = append(resp.Accounts, acc) } @@ -1297,12 +1303,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) return result, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, order.ErrPositionLiquidated) } for i := range info.Positions { - var pair currency.Pair - pair, err = currency.NewPairFromString(info.Positions[i].Future) - if err != nil { - return nil, err - } - if !pnl.Pair.Equal(pair) { + if !pnl.Pair.Equal(info.Positions[i].Future) { continue } if info.Positions[i].EntryPrice != ep { @@ -1323,131 +1324,314 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) } // ScaleCollateral takes your totals and scales them according to FTX's rules -func (f *FTX) ScaleCollateral(ctx context.Context, subAccount string, calc *order.CollateralCalculator) (decimal.Decimal, error) { - var result decimal.Decimal +func (f *FTX) ScaleCollateral(ctx context.Context, subAccount string, calc *order.CollateralCalculator) (*order.CollateralByCurrency, error) { if calc.CalculateOffline { - if calc.CollateralCurrency.Match(currency.USD) { + result := &order.CollateralByCurrency{ + Currency: calc.CollateralCurrency, + TotalFunds: calc.FreeCollateral.Add(calc.LockedCollateral), + AvailableForUseAsCollateral: calc.FreeCollateral, + FairMarketValue: calc.USDPrice, + ScaledCurrency: currency.USD, + UnrealisedPNL: calc.UnrealisedPNL, + ScaledUsed: calc.LockedCollateral, + } + if calc.CollateralCurrency.Equal(currency.USD) { // FTX bases scales all collateral into USD amounts - return calc.CollateralAmount, nil + result.CollateralContribution = calc.FreeCollateral + result.Weighting = decimal.NewFromInt(1) + result.FairMarketValue = decimal.NewFromInt(1) + return result, nil } - if calc.CollateralPrice.IsZero() { - return decimal.Zero, fmt.Errorf("%s %s %w to scale collateral", f.Name, calc.CollateralCurrency, order.ErrUSDValueRequired) + result.ScaledCurrency = currency.USD + if calc.USDPrice.IsZero() { + return nil, fmt.Errorf("%s %s %w to scale collateral", f.Name, calc.CollateralCurrency, order.ErrUSDValueRequired) } - collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Upper().String()] + if calc.FreeCollateral.IsZero() && calc.LockedCollateral.IsZero() { + return result, nil + } + collateralWeight, ok := f.collateralWeight[calc.CollateralCurrency.Item] if !ok { - return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) + return nil, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) } - if calc.CollateralAmount.IsPositive() { + if calc.FreeCollateral.IsPositive() { if collateralWeight.InitialMarginFractionFactor == 0 { - return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralInitialMarginFractionMissing) + return nil, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralInitialMarginFractionMissing) } var scaling decimal.Decimal - if calc.IsLiquidating { - scaling = decimal.NewFromFloat(collateralWeight.Total) - } else { + if calc.IsForNewPosition { scaling = decimal.NewFromFloat(collateralWeight.Initial) + } else { + scaling = decimal.NewFromFloat(collateralWeight.Total) } + if scaling.IsZero() { + result.SkipContribution = true + } + result.Weighting = scaling one := decimal.NewFromInt(1) - sqrt := decimal.NewFromFloat(math.Sqrt(calc.CollateralAmount.InexactFloat64())) + freeSqrt := decimal.NewFromFloat(math.Sqrt(calc.FreeCollateral.InexactFloat64())) + lockedSqrt := decimal.NewFromFloat(math.Sqrt(calc.LockedCollateral.InexactFloat64())) onePointOne := decimal.NewFromFloat(1.1) imf := decimal.NewFromFloat(collateralWeight.InitialMarginFractionFactor) - weight := onePointOne.Div(one.Add(imf.Mul(sqrt))) - result = calc.CollateralAmount.Mul(calc.CollateralPrice).Mul(decimal.Min(scaling, weight)) + freeWeight := onePointOne.Div(one.Add(imf.Mul(freeSqrt))) + lockedWeight := onePointOne.Div(one.Add(imf.Mul(lockedSqrt))) + result.CollateralContribution = calc.FreeCollateral.Mul(calc.USDPrice).Mul(decimal.Min(scaling, freeWeight)) + result.ScaledUsed = calc.LockedCollateral.Mul(calc.USDPrice).Mul(decimal.Min(scaling, lockedWeight)) } else { - result = result.Add(calc.CollateralAmount.Mul(calc.CollateralPrice)) + result.CollateralContribution = calc.FreeCollateral.Mul(calc.USDPrice) + result.ScaledUsed = calc.LockedCollateral.Mul(calc.USDPrice) + } + + if !result.UnrealisedPNL.IsZero() && result.ScaledUsedBreakdown != nil { + result.CollateralContribution = decimal.Min(result.CollateralContribution, result.CollateralContribution.Sub(result.UnrealisedPNL)).Sub(result.ScaledUsedBreakdown.LockedAsCollateral) } + return result, nil } - wallet, err := f.GetCoins(ctx, subAccount) - if err != nil { - return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, err) - } - balances, err := f.GetBalances(ctx, subAccount) + resp, err := f.calculateTotalCollateralOnline(ctx, + &order.TotalCollateralCalculator{ + SubAccount: subAccount, + CollateralAssets: []order.CollateralCalculator{*calc}, + }, + nil, + ) if err != nil { - return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, err) + return nil, err } - for i := range wallet { - if !currency.NewCode(wallet[i].ID).Match(calc.CollateralCurrency) { - continue - } - for j := range balances { - if !currency.NewCode(balances[j].Coin).Match(calc.CollateralCurrency) { - continue - } - scaled := wallet[i].CollateralWeight * balances[j].USDValue - result = decimal.NewFromFloat(scaled) - return result, nil - } + if len(resp.BreakdownByCurrency) == 0 { + return nil, fmt.Errorf("%v %v %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) } - return decimal.Zero, fmt.Errorf("%s %s %w", f.Name, calc.CollateralCurrency, errCollateralCurrencyNotFound) + return &resp.BreakdownByCurrency[0], nil } // CalculateTotalCollateral scales collateral and determines how much collateral you can use for positions -func (f *FTX) CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, collateralAssets []order.CollateralCalculator) (*order.TotalCollateralResponse, error) { - var result order.TotalCollateralResponse - if !calculateOffline { - wallet, err := f.GetCoins(ctx, subAccount) - if err != nil { - return nil, fmt.Errorf("%s %w", f.Name, err) - } - balances, err := f.GetBalances(ctx, subAccount) +func (f *FTX) CalculateTotalCollateral(ctx context.Context, calc *order.TotalCollateralCalculator) (*order.TotalCollateralResponse, error) { + if calc == nil { + return nil, fmt.Errorf("%v CalculateTotalCollateral %w", f.Name, common.ErrNilPointer) + } + var pos []PositionData + var err error + if calc.FetchPositions { + pos, err = f.GetPositions(ctx) if err != nil { - return nil, fmt.Errorf("%s %w", f.Name, err) + return nil, fmt.Errorf("%v CalculateTotalCollateral GetPositions %w", f.Name, err) } - for x := range collateralAssets { - wallets: - for y := range wallet { - if !currency.NewCode(wallet[y].ID).Match(collateralAssets[x].CollateralCurrency) { + } + if !calc.CalculateOffline { + return f.calculateTotalCollateralOnline(ctx, calc, pos) + } + + result := order.TotalCollateralResponse{ + CollateralCurrency: currency.USD, + } + for i := range calc.CollateralAssets { + if len(pos) > 0 { + // ensure we use supplied position data + calc.CollateralAssets[i].UnrealisedPNL = decimal.Zero + for j := range pos { + if !pos[j].Future.Base.Equal(calc.CollateralAssets[i].CollateralCurrency) { continue } - for z := range balances { - if !currency.NewCode(balances[z].Coin).Match(collateralAssets[x].CollateralCurrency) { - continue - } - scaled := wallet[y].CollateralWeight * balances[z].USDValue - dScaled := decimal.NewFromFloat(scaled) - result.TotalCollateral = result.TotalCollateral.Add(dScaled) - breakDown := order.CollateralByCurrency{ - Currency: collateralAssets[x].CollateralCurrency, - - OriginalValue: collateralAssets[x].CollateralAmount, - } - if !collateralAssets[x].CollateralCurrency.Match(currency.USD) { - breakDown.ScaledValue = dScaled - breakDown.ValueCurrency = currency.USD - } - result.BreakdownByCurrency = append(result.BreakdownByCurrency, breakDown) - break wallets - } + calc.CollateralAssets[i].UnrealisedPNL = calc.CollateralAssets[i].UnrealisedPNL.Add(decimal.NewFromFloat(pos[j].UnrealizedPNL)) } } - return &result, nil - } - for i := range collateralAssets { - curr := order.CollateralByCurrency{ - Currency: collateralAssets[i].CollateralCurrency, - } - collateral, err := f.ScaleCollateral(ctx, subAccount, &collateralAssets[i]) + var collateralByCurrency *order.CollateralByCurrency + collateralByCurrency, err = f.ScaleCollateral(ctx, calc.SubAccount, &calc.CollateralAssets[i]) if err != nil { if errors.Is(err, errCollateralCurrencyNotFound) { log.Error(log.ExchangeSys, err) continue } if errors.Is(err, order.ErrUSDValueRequired) { - curr.Error = err - result.BreakdownByCurrency = append(result.BreakdownByCurrency, curr) + if collateralByCurrency == nil { + return nil, err + } + collateralByCurrency.Error = err + result.BreakdownByCurrency = append(result.BreakdownByCurrency, *collateralByCurrency) continue } return nil, err } - result.TotalCollateral = result.TotalCollateral.Add(collateral) - curr.ScaledValue = collateral - if !collateralAssets[i].CollateralCurrency.Match(currency.USD) { - curr.ValueCurrency = currency.USD + + result.AvailableCollateral = result.AvailableCollateral.Add(collateralByCurrency.CollateralContribution) + result.UnrealisedPNL = result.UnrealisedPNL.Add(collateralByCurrency.UnrealisedPNL) + if collateralByCurrency.SkipContribution { + continue } + result.UsedCollateral = result.UsedCollateral.Add(collateralByCurrency.ScaledUsed) + result.BreakdownByCurrency = append(result.BreakdownByCurrency, *collateralByCurrency) + } + if !result.UnrealisedPNL.IsZero() && result.UsedBreakdown != nil { + result.AvailableCollateral = decimal.Min(result.AvailableCollateral, result.AvailableCollateral.Add(result.UnrealisedPNL)).Sub(result.UsedBreakdown.LockedAsCollateral) + } + return &result, nil +} - result.BreakdownByCurrency = append(result.BreakdownByCurrency, curr) +func (f *FTX) calculateTotalCollateralOnline(ctx context.Context, calc *order.TotalCollateralCalculator, pos []PositionData) (*order.TotalCollateralResponse, error) { + if calc == nil { + return nil, fmt.Errorf("%v CalculateTotalCollateral %w", f.Name, common.ErrNilPointer) } + if len(calc.CollateralAssets) == 0 { + return nil, fmt.Errorf("%v calculateTotalCollateralOnline %w, no currencies supplied", f.Name, errCollateralCurrencyNotFound) + } + if calc.CalculateOffline { + return nil, fmt.Errorf("%v calculateTotalCollateralOnline %w", f.Name, order.ErrOfflineCalculationSet) + } + + c, err := f.GetCollateral(ctx, false) + if err != nil { + return nil, fmt.Errorf("%s %w", f.Name, err) + } + mc, err := f.GetCollateral(ctx, true) + if err != nil { + return nil, fmt.Errorf("%s %w", f.Name, err) + } + result := order.TotalCollateralResponse{ + CollateralCurrency: currency.USD, + AvailableCollateral: c.CollateralAvailable, + AvailableMaintenanceCollateral: mc.CollateralAvailable, + TotalValueOfPositiveSpotBalances: c.PositiveSpotBalanceTotal, + CollateralContributedByPositiveSpotBalances: c.CollateralFromPositiveSpotBalances, + } + balances, err := f.GetBalances(ctx, calc.SubAccount, true, true) + if err != nil { + return nil, fmt.Errorf("%s %w", f.Name, err) + } + + for x := range calc.CollateralAssets { + if calc.CollateralAssets[x].CalculateOffline { + return nil, fmt.Errorf("%v %v %v calculateTotalCollateralOnline %w", f.Name, calc.CollateralAssets[x].Asset, calc.CollateralAssets[x].CollateralCurrency, order.ErrOfflineCalculationSet) + } + currencyBreakdown := order.CollateralByCurrency{ + Currency: calc.CollateralAssets[x].CollateralCurrency, + TotalFunds: calc.CollateralAssets[x].FreeCollateral.Add(calc.CollateralAssets[x].LockedCollateral), + AvailableForUseAsCollateral: calc.CollateralAssets[x].FreeCollateral, + ScaledCurrency: currency.USD, + } + if len(pos) > 0 { + // use pos unrealisedPNL, not calc.collateralAssets' + calc.CollateralAssets[x].UnrealisedPNL = decimal.Zero + for i := range pos { + if !pos[i].Future.Base.Equal(calc.CollateralAssets[x].CollateralCurrency) { + continue + } + calc.CollateralAssets[x].UnrealisedPNL = calc.CollateralAssets[x].UnrealisedPNL.Add(decimal.NewFromFloat(pos[i].UnrealizedPNL)) + } + } + currencyBreakdown.UnrealisedPNL = calc.CollateralAssets[x].UnrealisedPNL + + for y := range c.PositiveBalances { + if !c.PositiveBalances[y].Coin.Equal(calc.CollateralAssets[x].CollateralCurrency) { + continue + } + currencyBreakdown.Weighting = c.PositiveBalances[y].CollateralWeight + currencyBreakdown.FairMarketValue = c.PositiveBalances[y].ApproximateFairMarketValue + currencyBreakdown.CollateralContribution = c.PositiveBalances[y].AvailableIgnoringCollateral.Mul(c.PositiveBalances[y].ApproximateFairMarketValue).Mul(currencyBreakdown.Weighting) + currencyBreakdown.AdditionalCollateralUsed = c.PositiveBalances[y].CollateralUsed + currencyBreakdown.FairMarketValue = c.PositiveBalances[y].ApproximateFairMarketValue + currencyBreakdown.AvailableForUseAsCollateral = c.PositiveBalances[y].AvailableIgnoringCollateral + } + for y := range c.NegativeBalances { + if !c.NegativeBalances[y].Coin.Equal(calc.CollateralAssets[x].CollateralCurrency) { + continue + } + currencyBreakdown.Weighting = c.NegativeBalances[y].CollateralWeight + currencyBreakdown.FairMarketValue = c.NegativeBalances[y].ApproximateFairMarketValue + currencyBreakdown.CollateralContribution = c.NegativeBalances[y].AvailableIgnoringCollateral.Mul(c.NegativeBalances[y].ApproximateFairMarketValue).Mul(currencyBreakdown.Weighting) + currencyBreakdown.AdditionalCollateralUsed = c.NegativeBalances[y].CollateralUsed + currencyBreakdown.FairMarketValue = c.NegativeBalances[y].ApproximateFairMarketValue + currencyBreakdown.AvailableForUseAsCollateral = c.NegativeBalances[y].AvailableIgnoringCollateral + } + if currencyBreakdown.Weighting.IsZero() { + currencyBreakdown.SkipContribution = true + } + + for y := range balances { + // used to determine how collateral is being used + if !balances[y].Coin.Equal(calc.CollateralAssets[x].CollateralCurrency) { + continue + } + // staked values are in their own currency, scale it + lockedS := decimal.NewFromFloat(balances[y].LockedBreakdown.LockedInStakes) + lockedC := decimal.NewFromFloat(balances[y].LockedBreakdown.LockedAsCollateral) + lockedF := decimal.NewFromFloat(balances[y].LockedBreakdown.LockedInFeeVoucher) + lockedN := decimal.NewFromFloat(balances[y].LockedBreakdown.LockedInNFTBids) + lockedO := decimal.NewFromFloat(balances[y].LockedBreakdown.LockedInSpotOrders) + lockedFO := decimal.NewFromFloat(balances[y].LockedBreakdown.LockedInSpotMarginFundingOffers) + locked := decimal.Sum(lockedS, lockedC, lockedF, lockedN, lockedO, lockedFO) + if !locked.IsZero() || balances[y].SpotBorrow > 0 { + if result.UsedBreakdown == nil { + result.UsedBreakdown = &order.UsedCollateralBreakdown{} + } + var resetWeightingToZero bool + if currencyBreakdown.Weighting.IsZero() { + // this is to ensure we're not hiding any locked values + // when collateral contribution is zero (eg FTT with collateral disabled) + resetWeightingToZero = true + currencyBreakdown.Weighting = decimal.NewFromInt(1) + } + var resetFairMarketToZero bool + if currencyBreakdown.FairMarketValue.IsZero() { + // this is another edge case for SRM_LOCKED rendering locked data + currencyBreakdown.SkipContribution = true + resetFairMarketToZero = true + currencyBreakdown.FairMarketValue = decimal.NewFromInt(1) + } + currencyBreakdown.ScaledUsedBreakdown = &order.UsedCollateralBreakdown{ + LockedInStakes: lockedS.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting), + LockedInNFTBids: lockedN.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting), + LockedInFeeVoucher: lockedF.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting), + LockedInSpotMarginFundingOffers: lockedFO.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting), + LockedInSpotOrders: lockedO.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting), + LockedAsCollateral: lockedC.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting), + } + + if resetWeightingToZero { + currencyBreakdown.Weighting = decimal.Zero + } + if resetFairMarketToZero { + currencyBreakdown.FairMarketValue = decimal.Zero + } + + currencyBreakdown.ScaledUsed = locked.Mul(currencyBreakdown.FairMarketValue).Mul(currencyBreakdown.Weighting) + if balances[y].SpotBorrow > 0 { + currencyBreakdown.ScaledUsedBreakdown.UsedInSpotMarginBorrows = currencyBreakdown.CollateralContribution.Abs().Add(currencyBreakdown.AdditionalCollateralUsed) + currencyBreakdown.ScaledUsed = currencyBreakdown.ScaledUsed.Add(currencyBreakdown.ScaledUsedBreakdown.UsedInSpotMarginBorrows) + } + if !currencyBreakdown.SkipContribution { + result.UsedCollateral = result.UsedCollateral.Add(currencyBreakdown.ScaledUsed) + result.UsedBreakdown.LockedInStakes = result.UsedBreakdown.LockedInStakes.Add(currencyBreakdown.ScaledUsedBreakdown.LockedInStakes) + result.UsedBreakdown.LockedAsCollateral = result.UsedBreakdown.LockedAsCollateral.Add(currencyBreakdown.ScaledUsedBreakdown.LockedAsCollateral) + result.UsedBreakdown.LockedInFeeVoucher = result.UsedBreakdown.LockedInFeeVoucher.Add(currencyBreakdown.ScaledUsedBreakdown.LockedInFeeVoucher) + result.UsedBreakdown.LockedInNFTBids = result.UsedBreakdown.LockedInNFTBids.Add(currencyBreakdown.ScaledUsedBreakdown.LockedInNFTBids) + result.UsedBreakdown.LockedInSpotOrders = result.UsedBreakdown.LockedInSpotOrders.Add(currencyBreakdown.ScaledUsedBreakdown.LockedInSpotOrders) + result.UsedBreakdown.LockedInSpotMarginFundingOffers = result.UsedBreakdown.LockedInSpotMarginFundingOffers.Add(currencyBreakdown.ScaledUsedBreakdown.LockedInSpotMarginFundingOffers) + result.UsedBreakdown.UsedInSpotMarginBorrows = result.UsedBreakdown.UsedInSpotMarginBorrows.Add(currencyBreakdown.ScaledUsedBreakdown.UsedInSpotMarginBorrows) + } + } + } + if calc.CollateralAssets[x].CollateralCurrency.Equal(currency.USD) { + for y := range c.Positions { + if result.UsedBreakdown == nil { + result.UsedBreakdown = &order.UsedCollateralBreakdown{} + } + result.UsedBreakdown.UsedInPositions = result.UsedBreakdown.UsedInPositions.Add(c.Positions[y].CollateralUsed) + } + } + result.BreakdownByCurrency = append(result.BreakdownByCurrency, currencyBreakdown) + } + + for y := range c.Positions { + result.BreakdownOfPositions = append(result.BreakdownOfPositions, order.CollateralByPosition{ + PositionCurrency: c.Positions[y].Future, + Size: c.Positions[y].Size, + OpenOrderSize: c.Positions[y].OpenOrderSize, + PositionSize: c.Positions[y].PositionSize, + MarkPrice: c.Positions[y].MarkPrice, + RequiredMargin: c.Positions[y].RequiredMargin, + CollateralUsed: c.Positions[y].CollateralUsed, + }) + } + return &result, nil } @@ -1456,7 +1640,7 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency if !a.IsFutures() { return nil, fmt.Errorf("%w futures asset type only", common.ErrFunctionNotSupported) } - fills, err := f.GetFills(ctx, cp, a, "200", start, end) + fills, err := f.GetFills(ctx, cp, a, start, end) if err != nil { return nil, err } diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index 774974e2844..705c8f0277b 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -506,7 +506,7 @@ func TestGetOrderInfo(t *testing.T) { } _, err := g.GetOrderInfo(context.Background(), - "917591554", currency.Pair{}, asset.Spot) + "917591554", currency.EMPTYPAIR, asset.Spot) if err != nil { if err.Error() != "no order found with id 917591554" && err.Error() != "failed to get open orders" { t.Fatalf("GetOrderInfo() returned an error skipping test: %v", err) diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 4491da84fd6..ebf97e5be7d 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -130,8 +130,11 @@ func (g *Gateio) SetDefaults() { }, }, } - g.Requester = request.New(g.Name, + g.Requester, err = request.New(g.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } g.API.Endpoints = g.NewEndpoints() err = g.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: gateioTradeURL, @@ -351,8 +354,9 @@ func (g *Gateio) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a for k := range resp.Result { currData = append(currData, account.Balance{ CurrencyName: currency.NewCode(k), - TotalValue: resp.Result[k].Available + resp.Result[k].Freeze, + Total: resp.Result[k].Available + resp.Result[k].Freeze, Hold: resp.Result[k].Freeze, + Free: resp.Result[k].Available, }) } info.Accounts = append(info.Accounts, account.SubAccount{ @@ -367,7 +371,8 @@ func (g *Gateio) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a switch l := balance.Locked.(type) { case map[string]interface{}: for x := range l { - lockedF, err := strconv.ParseFloat(l[x].(string), 64) + var lockedF float64 + lockedF, err = strconv.ParseFloat(l[x].(string), 64) if err != nil { return info, err } @@ -384,23 +389,27 @@ func (g *Gateio) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a switch v := balance.Available.(type) { case map[string]interface{}: for x := range v { - availAmount, err := strconv.ParseFloat(v[x].(string), 64) + var availAmount float64 + availAmount, err = strconv.ParseFloat(v[x].(string), 64) if err != nil { return info, err } var updated bool for i := range balances { - if balances[i].CurrencyName == currency.NewCode(x) { - balances[i].TotalValue = balances[i].Hold + availAmount - updated = true - break + if !balances[i].CurrencyName.Equal(currency.NewCode(x)) { + continue } + balances[i].Total = balances[i].Hold + availAmount + balances[i].Free = availAmount + balances[i].AvailableWithoutBorrow = availAmount + updated = true + break } if !updated { balances = append(balances, account.Balance{ CurrencyName: currency.NewCode(x), - TotalValue: availAmount, + Total: availAmount, }) } } diff --git a/exchanges/gemini/gemini_mock_test.go b/exchanges/gemini/gemini_mock_test.go index b2ee314d8f8..05b19df2e14 100644 --- a/exchanges/gemini/gemini_mock_test.go +++ b/exchanges/gemini/gemini_mock_test.go @@ -45,7 +45,10 @@ func TestMain(m *testing.M) { log.Fatalf("Mock server error %s", err) } - g.HTTPClient = newClient + err = g.SetHTTPClient(newClient) + if err != nil { + log.Fatalf("Mock server error %s", err) + } endpointMap := g.API.Endpoints.GetURLMap() for k := range endpointMap { err = g.API.Endpoints.SetRunning(k, serverDetails) diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 0a6c1e75f65..f0f0d2cb891 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -113,9 +113,12 @@ func (g *Gemini) SetDefaults() { }, } - g.Requester = request.New(g.Name, + g.Requester, err = request.New(g.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } g.API.Endpoints = g.NewEndpoints() err = g.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: geminiAPIURL, @@ -318,11 +321,12 @@ func (g *Gemini) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a var currencies []account.Balance for i := range accountBalance { - var exchangeCurrency account.Balance - exchangeCurrency.CurrencyName = currency.NewCode(accountBalance[i].Currency) - exchangeCurrency.TotalValue = accountBalance[i].Amount - exchangeCurrency.Hold = accountBalance[i].Amount - accountBalance[i].Available - currencies = append(currencies, exchangeCurrency) + currencies = append(currencies, account.Balance{ + CurrencyName: currency.NewCode(accountBalance[i].Currency), + Total: accountBalance[i].Amount, + Hold: accountBalance[i].Amount - accountBalance[i].Available, + Free: accountBalance[i].Available, + }) } response.Accounts = append(response.Accounts, account.SubAccount{ diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index cc438d0c4b8..725d46897b9 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -129,9 +129,12 @@ func (h *HitBTC) SetDefaults() { }, } - h.Requester = request.New(h.Name, + h.Requester, err = request.New(h.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } h.API.Endpoints = h.NewEndpoints() err = h.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: apiURL, @@ -435,11 +438,12 @@ func (h *HitBTC) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a var currencies []account.Balance for i := range accountBalance { - var exchangeCurrency account.Balance - exchangeCurrency.CurrencyName = currency.NewCode(accountBalance[i].Currency) - exchangeCurrency.TotalValue = accountBalance[i].Available - exchangeCurrency.Hold = accountBalance[i].Reserved - currencies = append(currencies, exchangeCurrency) + currencies = append(currencies, account.Balance{ + CurrencyName: currency.NewCode(accountBalance[i].Currency), + Total: accountBalance[i].Available + accountBalance[i].Reserved, + Hold: accountBalance[i].Reserved, + Free: accountBalance[i].Available, + }) } response.Accounts = append(response.Accounts, account.SubAccount{ diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index b852edcab81..5a36b1c9716 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -101,7 +101,7 @@ func TestStart(t *testing.T) { func TestGetCurrenciesIncludingChains(t *testing.T) { t.Parallel() - r, err := h.GetCurrenciesIncludingChains(context.Background(), currency.Code{}) + r, err := h.GetCurrenciesIncludingChains(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -119,7 +119,7 @@ func TestGetCurrenciesIncludingChains(t *testing.T) { func TestFGetContractInfo(t *testing.T) { t.Parallel() - _, err := h.FGetContractInfo(context.Background(), "", "", currency.Pair{}) + _, err := h.FGetContractInfo(context.Background(), "", "", currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -136,7 +136,7 @@ func TestFIndexPriceInfo(t *testing.T) { func TestFContractPriceLimitations(t *testing.T) { t.Parallel() _, err := h.FContractPriceLimitations(context.Background(), - "BTC", "next_quarter", currency.Pair{}) + "BTC", "next_quarter", currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -145,7 +145,7 @@ func TestFContractPriceLimitations(t *testing.T) { func TestFContractOpenInterest(t *testing.T) { t.Parallel() _, err := h.FContractOpenInterest(context.Background(), - "BTC", "next_quarter", currency.Pair{}) + "BTC", "next_quarter", currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -289,7 +289,7 @@ func TestFGetAccountInfo(t *testing.T) { t.Skip("skipping test: api keys not set") } t.Parallel() - _, err := h.FGetAccountInfo(context.Background(), currency.Code{}) + _, err := h.FGetAccountInfo(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -300,7 +300,7 @@ func TestFGetPositionsInfo(t *testing.T) { t.Skip("skipping test: api keys not set") } t.Parallel() - _, err := h.FGetPositionsInfo(context.Background(), currency.Code{}) + _, err := h.FGetPositionsInfo(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -311,7 +311,7 @@ func TestFGetAllSubAccountAssets(t *testing.T) { t.Skip("skipping test: api keys not set") } t.Parallel() - _, err := h.FGetAllSubAccountAssets(context.Background(), currency.Code{}) + _, err := h.FGetAllSubAccountAssets(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -368,7 +368,7 @@ func TestFContractTradingFee(t *testing.T) { t.Skip("skipping test: api keys not set") } t.Parallel() - _, err := h.FContractTradingFee(context.Background(), currency.Code{}) + _, err := h.FContractTradingFee(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -379,7 +379,7 @@ func TestFGetTransferLimits(t *testing.T) { t.Skip("skipping test: api keys not set") } t.Parallel() - _, err := h.FGetTransferLimits(context.Background(), currency.Code{}) + _, err := h.FGetTransferLimits(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -390,7 +390,7 @@ func TestFGetPositionLimits(t *testing.T) { t.Skip("skipping test: api keys not set") } t.Parallel() - _, err := h.FGetPositionLimits(context.Background(), currency.Code{}) + _, err := h.FGetPositionLimits(context.Background(), currency.EMPTYCODE) if err != nil { t.Error(err) } @@ -460,7 +460,7 @@ func TestFOrder(t *testing.T) { t.Error(err) } _, err = h.FOrder(context.Background(), - currency.Pair{}, cp.Base.Upper().String(), + currency.EMPTYPAIR, cp.Base.Upper().String(), "quarter", "123", "BUY", "open", "limit", 1, 1, 1) if err != nil { t.Error(err) @@ -542,7 +542,7 @@ func TestFFlashCloseOrder(t *testing.T) { } t.Parallel() _, err := h.FFlashCloseOrder(context.Background(), - currency.Pair{}, "BTC", "quarter", "BUY", "lightning", "", 1) + currency.EMPTYPAIR, "BTC", "quarter", "BUY", "lightning", "", 1) if err != nil { t.Error(err) } @@ -600,7 +600,7 @@ func TestFGetOrderHistory(t *testing.T) { t.Error(err) } _, err = h.FGetOrderHistory(context.Background(), - currency.Pair{}, cp.Base.Upper().String(), + currency.EMPTYPAIR, cp.Base.Upper().String(), "all", "all", "limit", []order.Status{}, 5, 0, 0) @@ -615,7 +615,7 @@ func TestFTradeHistory(t *testing.T) { } t.Parallel() _, err := h.FTradeHistory(context.Background(), - currency.Pair{}, "BTC", "all", 10, 0, 0) + currency.EMPTYPAIR, "BTC", "all", 10, 0, 0) if err != nil { t.Error(err) } @@ -627,7 +627,7 @@ func TestFPlaceTriggerOrder(t *testing.T) { } t.Parallel() _, err := h.FPlaceTriggerOrder(context.Background(), - currency.Pair{}, "EOS", "quarter", "greaterOrEqual", + currency.EMPTYPAIR, "EOS", "quarter", "greaterOrEqual", "limit", "buy", "close", 1.1, 1.05, 5, 2) if err != nil { t.Error(err) @@ -651,7 +651,7 @@ func TestFCancelAllTriggerOrders(t *testing.T) { } t.Parallel() _, err := h.FCancelAllTriggerOrders(context.Background(), - currency.Pair{}, "BTC", "this_week") + currency.EMPTYPAIR, "BTC", "this_week") if err != nil { t.Error(err) } @@ -663,7 +663,7 @@ func TestFQueryTriggerOpenOrders(t *testing.T) { } t.Parallel() _, err := h.FQueryTriggerOpenOrders(context.Background(), - currency.Pair{}, "BTC", 0, 0) + currency.EMPTYPAIR, "BTC", 0, 0) if err != nil { t.Error(err) } @@ -675,7 +675,7 @@ func TestFQueryTriggerOrderHistory(t *testing.T) { } t.Parallel() _, err := h.FQueryTriggerOrderHistory(context.Background(), - currency.Pair{}, "EOS", "all", "all", 10, 0, 0) + currency.EMPTYPAIR, "EOS", "all", "all", 10, 0, 0) if err != nil { t.Error(err) } @@ -1571,7 +1571,7 @@ func TestGetSwapTriggerOrderHistory(t *testing.T) { func TestGetSwapMarkets(t *testing.T) { t.Parallel() - _, err := h.GetSwapMarkets(context.Background(), currency.Pair{}) + _, err := h.GetSwapMarkets(context.Background(), currency.EMPTYPAIR) if err != nil { t.Error(err) } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 025a6292559..97170c28b7f 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -158,9 +158,12 @@ func (h *HUOBI) SetDefaults() { }, } - h.Requester = request.New(h.Name, + h.Requester, err = request.New(h.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } h.API.Endpoints = h.NewEndpoints() err = h.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: huobiAPIURL, @@ -360,7 +363,7 @@ func (h *HUOBI) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, } case asset.CoinMarginedFutures: - symbols, err := h.GetSwapMarkets(ctx, currency.Pair{}) + symbols, err := h.GetSwapMarkets(ctx, currency.EMPTYPAIR) if err != nil { return nil, err } @@ -375,7 +378,7 @@ func (h *HUOBI) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, } } case asset.Futures: - symbols, err := h.FGetContractInfo(ctx, "", "", currency.Pair{}) + symbols, err := h.FGetContractInfo(ctx, "", "", currency.EMPTYPAIR) if err != nil { return nil, err } @@ -649,7 +652,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac } currData := account.Balance{ CurrencyName: currency.NewCode(resp.Data[i].List[0].Currency), - TotalValue: resp.Data[i].List[0].Balance, + Total: resp.Data[i].List[0].Balance, } if len(resp.Data[i].List) > 1 && resp.Data[i].List[1].Type == "frozen" { currData.Hold = resp.Data[i].List[1].Balance @@ -681,7 +684,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac if frozen { currencyDetails[i].Hold = balances[j].Balance } else { - currencyDetails[i].TotalValue = balances[j].Balance + currencyDetails[i].Total = balances[j].Balance } continue balance } @@ -697,7 +700,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac currencyDetails = append(currencyDetails, account.Balance{ CurrencyName: currency.NewCode(balances[j].Currency), - TotalValue: balances[j].Balance, + Total: balances[j].Balance, }) } } @@ -707,7 +710,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac case asset.CoinMarginedFutures: // fetch swap account info - acctInfo, err := h.GetSwapAccountInfo(ctx, currency.Pair{}) + acctInfo, err := h.GetSwapAccountInfo(ctx, currency.EMPTYPAIR) if err != nil { return info, err } @@ -716,8 +719,9 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac for x := range acctInfo.Data { mainAcctBalances = append(mainAcctBalances, account.Balance{ CurrencyName: currency.NewCode(acctInfo.Data[x].Symbol), - TotalValue: acctInfo.Data[x].MarginBalance, + Total: acctInfo.Data[x].MarginBalance, Hold: acctInfo.Data[x].MarginFrozen, + Free: acctInfo.Data[x].MarginAvailable, }) } @@ -727,14 +731,14 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac }) // fetch subaccounts data - subAccsData, err := h.GetSwapAllSubAccAssets(ctx, currency.Pair{}) + subAccsData, err := h.GetSwapAllSubAccAssets(ctx, currency.EMPTYPAIR) if err != nil { return info, err } var currencyDetails []account.Balance for x := range subAccsData.Data { a, err := h.SwapSingleSubAccAssets(ctx, - currency.Pair{}, + currency.EMPTYPAIR, subAccsData.Data[x].SubUID) if err != nil { return info, err @@ -742,15 +746,16 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac for y := range a.Data { currencyDetails = append(currencyDetails, account.Balance{ CurrencyName: currency.NewCode(a.Data[y].Symbol), - TotalValue: a.Data[y].MarginBalance, + Total: a.Data[y].MarginBalance, Hold: a.Data[y].MarginFrozen, + Free: a.Data[y].MarginAvailable, }) } } acc.Currencies = currencyDetails case asset.Futures: // fetch main account data - mainAcctData, err := h.FGetAccountInfo(ctx, currency.Code{}) + mainAcctData, err := h.FGetAccountInfo(ctx, currency.EMPTYCODE) if err != nil { return info, err } @@ -759,8 +764,9 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac for x := range mainAcctData.AccData { mainAcctBalances = append(mainAcctBalances, account.Balance{ CurrencyName: currency.NewCode(mainAcctData.AccData[x].Symbol), - TotalValue: mainAcctData.AccData[x].MarginBalance, + Total: mainAcctData.AccData[x].MarginBalance, Hold: mainAcctData.AccData[x].MarginFrozen, + Free: mainAcctData.AccData[x].MarginAvailable, }) } @@ -770,7 +776,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac }) // fetch subaccounts data - subAccsData, err := h.FGetAllSubAccountAssets(ctx, currency.Code{}) + subAccsData, err := h.FGetAllSubAccountAssets(ctx, currency.EMPTYCODE) if err != nil { return info, err } @@ -785,8 +791,9 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac for y := range a.AssetsData { currencyDetails = append(currencyDetails, account.Balance{ CurrencyName: currency.NewCode(a.AssetsData[y].Symbol), - TotalValue: a.AssetsData[y].MarginBalance, + Total: a.AssetsData[y].MarginBalance, Hold: a.AssetsData[y].MarginFrozen, + Free: a.AssetsData[y].MarginAvailable, }) } } @@ -946,9 +953,17 @@ func (h *HUOBI) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitR var oType string switch s.Type { case order.Market: - oType = "opponent" + // https://huobiapi.github.io/docs/dm/v1/en/#order-and-trade + // At present, Huobi Futures does not support market price when placing an order. + // To increase the probability of a transaction, users can choose to place an order based on BBO price (opponent), + // optimal 5 (optimal_5), optimal 10 (optimal_10), optimal 20 (optimal_20), among which the success probability of + // optimal 20 is the largest, while the slippage always is the largest as well. + // + // It is important to note that the above methods will not guarantee the order to be filled in 100%. + // The system will obtain the optimal N price at that moment and place the order. + oType = "optimal_20" if s.ImmediateOrCancel { - oType = "opponent_ioc" + oType = "optimal_20_ioc" } case order.Limit: oType = "limit" diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index 70cf3b74fab..824567189ae 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -68,8 +68,8 @@ type IBotExchange interface { WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) - SetHTTPClientUserAgent(ua string) - GetHTTPClientUserAgent() string + SetHTTPClientUserAgent(ua string) error + GetHTTPClientUserAgent() (string, error) SetClientProxyAddress(addr string) error SupportsREST() bool GetSubscriptions() ([]stream.ChannelSubscription, error) diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index 95fb3e4c1dc..5886330f21b 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -423,7 +423,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, bankTransactionType exch var fee float64 if (bankTransactionType == exchange.Swift || bankTransactionType == exchange.WireTransfer) && - c.Match(currency.USD) { + c.Equal(currency.USD) { fee = 40 } else if (bankTransactionType == exchange.SEPA || bankTransactionType == exchange.WireTransfer) && diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 79c6524c0c5..a88b07e0a9e 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -94,8 +94,11 @@ func (i *ItBit) SetDefaults() { }, } - i.Requester = request.New(i.Name, + i.Requester, err = request.New(i.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } i.API.Endpoints = i.NewEndpoints() err = i.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: itbitAPIURL, @@ -271,21 +274,17 @@ func (i *ItBit) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac return info, err } - type balance struct { - TotalValue float64 - Hold float64 - } - - var amounts = make(map[string]*balance) + var amounts = make(map[string]*account.Balance) for x := range wallets { for _, cb := range wallets[x].Balances { if _, ok := amounts[cb.Currency]; !ok { - amounts[cb.Currency] = &balance{} + amounts[cb.Currency] = &account.Balance{} } - amounts[cb.Currency].TotalValue += cb.TotalBalance + amounts[cb.Currency].Total += cb.TotalBalance amounts[cb.Currency].Hold += cb.TotalBalance - cb.AvailableBalance + amounts[cb.Currency].Free += cb.AvailableBalance } } @@ -293,8 +292,9 @@ func (i *ItBit) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac for key := range amounts { fullBalance = append(fullBalance, account.Balance{ CurrencyName: currency.NewCode(key), - TotalValue: amounts[key].TotalValue, + Total: amounts[key].Total, Hold: amounts[key].Hold, + Free: amounts[key].Free, }) } diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index b5cbe68ab6a..dccdc104b2e 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -163,7 +163,7 @@ func TestWrapperGetOrderInfo(t *testing.T) { t.Skip("skipping test: api keys not set") } _, err := k.GetOrderInfo(context.Background(), - "123", currency.Pair{}, asset.Futures) + "123", currency.EMPTYPAIR, asset.Futures) if err != nil { t.Error(err) } @@ -882,7 +882,7 @@ func TestGetOrderInfo(t *testing.T) { } _, err := k.GetOrderInfo(context.Background(), - "OZPTPJ-HVYHF-EDIGXS", currency.Pair{}, asset.Spot) + "OZPTPJ-HVYHF-EDIGXS", currency.EMPTYPAIR, asset.Spot) if !areTestAPIKeysSet() && err == nil { t.Error("Expecting error") } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index a959e908db9..2cf42d12a00 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -171,9 +171,12 @@ func (k *Kraken) SetDefaults() { }, } - k.Requester = request.New(k.Name, + k.Requester, err = request.New(k.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(request.NewBasicRateLimit(krakenRateInterval, krakenRequestRate))) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } k.API.Endpoints = k.NewEndpoints() err = k.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: krakenAPIURL, @@ -595,7 +598,7 @@ func (k *Kraken) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a } balances = append(balances, account.Balance{ CurrencyName: currency.NewCode(translatedCurrency), - TotalValue: bal[key], + Total: bal[key], }) } info.Accounts = append(info.Accounts, account.SubAccount{ @@ -610,7 +613,7 @@ func (k *Kraken) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a for code := range bal.Accounts[name].Balances { balances = append(balances, account.Balance{ CurrencyName: currency.NewCode(code).Upper(), - TotalValue: bal.Accounts[name].Balances[code], + Total: bal.Accounts[name].Balances[code], }) } info.Accounts = append(info.Accounts, account.SubAccount{ diff --git a/exchanges/lbank/lbank_test.go b/exchanges/lbank/lbank_test.go index 4df70be1b6c..7cf5225c237 100644 --- a/exchanges/lbank/lbank_test.go +++ b/exchanges/lbank/lbank_test.go @@ -381,7 +381,7 @@ func TestGetOrderInfo(t *testing.T) { t.Skip("API keys required but not set, skipping test") } _, err := l.GetOrderInfo(context.Background(), - "9ead39f5-701a-400b-b635-d7349eb0f6b", currency.Pair{}, asset.Spot) + "9ead39f5-701a-400b-b635-d7349eb0f6b", currency.EMPTYPAIR, asset.Spot) if err != nil { t.Error(err) } diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index 9aab8bccfee..8186548e850 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -108,8 +108,11 @@ func (l *Lbank) SetDefaults() { }, }, } - l.Requester = request.New(l.Name, + l.Requester, err = request.New(l.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } l.API.Endpoints = l.NewEndpoints() err = l.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: lbankAPIURL, @@ -336,8 +339,10 @@ func (l *Lbank) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac } acc.Currencies = append(acc.Currencies, account.Balance{ CurrencyName: c, - TotalValue: totalVal, - Hold: totalHold}) + Total: totalVal, + Hold: totalHold, + Free: totalVal - totalHold, + }) } info.Accounts = append(info.Accounts, acc) diff --git a/exchanges/localbitcoins/localbitcoins_mock_test.go b/exchanges/localbitcoins/localbitcoins_mock_test.go index e9799289532..688d85b8017 100644 --- a/exchanges/localbitcoins/localbitcoins_mock_test.go +++ b/exchanges/localbitcoins/localbitcoins_mock_test.go @@ -44,7 +44,10 @@ func TestMain(m *testing.M) { log.Fatalf("Mock server error %s", err) } - l.HTTPClient = newClient + err = l.SetHTTPClient(newClient) + if err != nil { + log.Fatalf("Mock server error %s", err) + } endpoints := l.API.Endpoints.GetURLMap() for k := range endpoints { err = l.API.Endpoints.SetRunning(k, serverDetails) diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 0dbafa91772..5a4e17d2db5 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -93,8 +93,11 @@ func (l *LocalBitcoins) SetDefaults() { }, } - l.Requester = request.New(l.Name, + l.Requester, err = request.New(l.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } l.API.Endpoints = l.NewEndpoints() err = l.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: localbitcoinsAPIURL, @@ -270,19 +273,22 @@ func (l *LocalBitcoins) UpdateOrderbook(ctx context.Context, p currency.Pair, as // UpdateAccountInfo retrieves balances for all enabled currencies for the // LocalBitcoins exchange -func (l *LocalBitcoins) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) { +func (l *LocalBitcoins) UpdateAccountInfo(ctx context.Context, _ asset.Item) (account.Holdings, error) { var response account.Holdings response.Exchange = l.Name accountBalance, err := l.GetWalletBalance(ctx) if err != nil { return response, err } - var exchangeCurrency account.Balance - exchangeCurrency.CurrencyName = currency.BTC - exchangeCurrency.TotalValue = accountBalance.Total.Balance response.Accounts = append(response.Accounts, account.SubAccount{ - Currencies: []account.Balance{exchangeCurrency}, + Currencies: []account.Balance{ + { + CurrencyName: currency.BTC, + Total: accountBalance.Total.Balance, + Hold: accountBalance.Total.Balance - accountBalance.Total.Sendable, + Free: accountBalance.Total.Sendable, + }}, }) err = account.Process(&response) diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index e110e3f6d54..f689de5c136 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -133,11 +133,14 @@ func (o *OKCoin) SetDefaults() { }, } - o.Requester = request.New(o.Name, + o.Requester, err = request.New(o.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), // TODO: Specify each individual endpoint rate limits as per docs request.WithLimiter(request.NewBasicRateLimit(okCoinRateInterval, okCoinStandardRequestRate)), ) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } o.API.Endpoints = o.NewEndpoints() err = o.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: okCoinAPIURL, diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index ca67f1e5aaa..c2638da2547 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -191,11 +191,14 @@ func (o *OKEX) SetDefaults() { }, } - o.Requester = request.New(o.Name, + o.Requester, err = request.New(o.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), // TODO: Specify each individual endpoint rate limits as per docs request.WithLimiter(request.NewBasicRateLimit(okExRateInterval, okExRequestRate)), ) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } o.API.Endpoints = o.NewEndpoints() err = o.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: okExAPIURL, diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okgroup/okgroup_websocket.go index 92d6c5ebe2a..0951aab99e6 100644 --- a/exchanges/okgroup/okgroup_websocket.go +++ b/exchanges/okgroup/okgroup_websocket.go @@ -847,7 +847,7 @@ func (o *OKGroup) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[y], - Currency: currency.NewPair(newP.Base, currency.Code{}), + Currency: currency.NewPair(newP.Base, currency.EMPTYCODE), Asset: asset.Futures, }) futuresAccountCodes = append(futuresAccountCodes, newP.Base) diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index 2b83923f352..41102e2d1e5 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -206,8 +206,9 @@ func (o *OKGroup) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( currencyAccount.Currencies = append(currencyAccount.Currencies, account.Balance{ CurrencyName: currency.NewCode(currencies[i].Currency), + Total: totalValue, Hold: hold, - TotalValue: totalValue, + Free: totalValue - hold, }) } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index f178b7c40f6..57f132c151f 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -12,7 +12,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -34,7 +33,7 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return fmt.Errorf("order %v %v %v %v %w", d.Exchange, d.AssetType, d.Pair, d.ID, ErrNotFuturesAsset) } if c == nil { - return common.ErrNilPointer + return fmt.Errorf("position controller %w", common.ErrNilPointer) } c.m.Lock() defer c.m.Unlock() @@ -69,7 +68,7 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { // exchange, asset pair that is stored in the position controller func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]PositionStats, error) { if c == nil { - return nil, common.ErrNilPointer + return nil, fmt.Errorf("position controller %w", common.ErrNilPointer) } c.m.Lock() defer c.m.Unlock() @@ -97,13 +96,14 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite // using the latest ticker data func (c *PositionController) UpdateOpenPositionUnrealisedPNL(exch string, item asset.Item, pair currency.Pair, last float64, updated time.Time) (decimal.Decimal, error) { if c == nil { - return decimal.Zero, common.ErrNilPointer + return decimal.Zero, fmt.Errorf("position controller %w", common.ErrNilPointer) } - c.m.Lock() - defer c.m.Unlock() if !item.IsFutures() { return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) } + + c.m.Lock() + defer c.m.Unlock() exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] if !ok { return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) @@ -117,14 +117,52 @@ func (c *PositionController) UpdateOpenPositionUnrealisedPNL(exch string, item a return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) } - return multiPositionTracker.UpdateOpenPositionUnrealisedPNL(last, updated) + multiPositionTracker.m.Lock() + defer multiPositionTracker.m.Unlock() + pos := multiPositionTracker.positions + if len(pos) == 0 { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForPair) + } + latestPos := pos[len(pos)-1] + if latestPos.status != Open { + return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionClosed) + } + err := latestPos.TrackPNLByTime(updated, last) + if err != nil { + return decimal.Zero, fmt.Errorf("%w for position %v %v %v", err, exch, item, pair) + } + latestPos.m.Lock() + defer latestPos.m.Unlock() + return latestPos.unrealisedPNL, nil +} + +// UpdateOpenPositionUnrealisedPNL updates the pnl for the latest open position +// based on the last price and the time +func (m *MultiPositionTracker) UpdateOpenPositionUnrealisedPNL(last float64, updated time.Time) (decimal.Decimal, error) { + m.m.Lock() + defer m.m.Unlock() + pos := m.positions + if len(pos) == 0 { + return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionsNotLoadedForPair) + } + latestPos := pos[len(pos)-1] + if latestPos.status != Open { + return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionClosed) + } + err := latestPos.TrackPNLByTime(updated, last) + if err != nil { + return decimal.Zero, fmt.Errorf("%w for position %v %v %v", err, m.exchange, m.asset, m.pair) + } + latestPos.m.Lock() + defer latestPos.m.Unlock() + return latestPos.unrealisedPNL, nil } // ClearPositionsForExchange resets positions for an // exchange, asset, pair that has been stored func (c *PositionController) ClearPositionsForExchange(exch string, item asset.Item, pair currency.Pair) error { if c == nil { - return common.ErrNilPointer + return fmt.Errorf("position controller %w", common.ErrNilPointer) } c.m.Lock() defer c.m.Unlock() @@ -192,82 +230,81 @@ func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPosition } // GetPositions returns all positions -func (m *MultiPositionTracker) GetPositions() []PositionStats { - if m == nil { +func (e *MultiPositionTracker) GetPositions() []PositionStats { + if e == nil { return nil } - m.m.Lock() - defer m.m.Unlock() + e.m.Lock() + defer e.m.Unlock() var resp []PositionStats - for i := range m.positions { - resp = append(resp, m.positions[i].GetStats()) + for i := range e.positions { + resp = append(resp, e.positions[i].GetStats()) } return resp } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices -func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { - if m == nil { - return common.ErrNilPointer +func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { + if e == nil { + return fmt.Errorf("multi-position tracker %w", common.ErrNilPointer) } if d == nil { return ErrSubmissionIsNil } - m.m.Lock() - defer m.m.Unlock() - if d.AssetType != m.asset { + e.m.Lock() + defer e.m.Unlock() + if d.AssetType != e.asset { return errAssetMismatch } - if tracker, ok := m.orderPositions[d.ID]; ok { + if tracker, ok := e.orderPositions[d.ID]; ok { // this has already been associated // update the tracker return tracker.TrackNewOrder(d) } - if len(m.positions) > 0 { - for i := range m.positions { - if m.positions[i].status == Open && i != len(m.positions)-1 { - return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, m.positions[i], i, len(m.positions)-1) + if len(e.positions) > 0 { + for i := range e.positions { + if e.positions[i].status == Open && i != len(e.positions)-1 { + return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) } } - if m.positions[len(m.positions)-1].status == Open { - err := m.positions[len(m.positions)-1].TrackNewOrder(d) + if e.positions[len(e.positions)-1].status == Open { + err := e.positions[len(e.positions)-1].TrackNewOrder(d) if err != nil && !errors.Is(err, ErrPositionClosed) { return err } - m.orderPositions[d.ID] = m.positions[len(m.positions)-1] + e.orderPositions[d.ID] = e.positions[len(e.positions)-1] return nil } } setup := &PositionTrackerSetup{ Pair: d.Pair, EntryPrice: decimal.NewFromFloat(d.Price), - EntryAmount: decimal.NewFromFloat(d.Amount), Underlying: d.Pair.Base, Asset: d.AssetType, Side: d.Side, - UseExchangePNLCalculation: m.useExchangePNLCalculations, + UseExchangePNLCalculation: e.useExchangePNLCalculations, } - tracker, err := m.SetupPositionTracker(setup) + tracker, err := e.SetupPositionTracker(setup) if err != nil { return err } - m.positions = append(m.positions, tracker) + e.positions = append(e.positions, tracker) err = tracker.TrackNewOrder(d) if err != nil { return err } - m.orderPositions[d.ID] = tracker + e.orderPositions[d.ID] = tracker return nil } // SetupPositionTracker creates a new position tracker to track n futures orders // until the position(s) are closed -func (m *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { - if m == nil { - return nil, common.ErrNilPointer +func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { + if e == nil { + return nil, fmt.Errorf("multi-position tracker %w", common.ErrNilPointer) } - if m.exchange == "" { + if e.exchange == "" { return nil, errExchangeNameEmpty } if setup == nil { @@ -281,52 +318,29 @@ func (m *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) } resp := &PositionTracker{ - exchange: strings.ToLower(m.exchange), + exchange: strings.ToLower(e.exchange), asset: setup.Asset, contractPair: setup.Pair, underlyingAsset: setup.Underlying, status: Open, entryPrice: setup.EntryPrice, - entryAmount: setup.EntryAmount, currentDirection: setup.Side, openingDirection: setup.Side, useExchangePNLCalculation: setup.UseExchangePNLCalculation, - offlinePNLCalculation: m.offlinePNLCalculation, + offlinePNLCalculation: e.offlinePNLCalculation, } if !setup.UseExchangePNLCalculation { // use position tracker's pnl calculation by default resp.PNLCalculation = &PNLCalculator{} } else { - if m.exchangePNLCalculation == nil { + if e.exchangePNLCalculation == nil { return nil, ErrNilPNLCalculator } - resp.PNLCalculation = m.exchangePNLCalculation + resp.PNLCalculation = e.exchangePNLCalculation } return resp, nil } -// UpdateOpenPositionUnrealisedPNL updates the pnl for the latest open position -// based on the last price and the time -func (m *MultiPositionTracker) UpdateOpenPositionUnrealisedPNL(last float64, updated time.Time) (decimal.Decimal, error) { - m.m.Lock() - defer m.m.Unlock() - pos := m.positions - if len(pos) == 0 { - return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionsNotLoadedForPair) - } - latestPos := pos[len(pos)-1] - if latestPos.status != Open { - return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionClosed) - } - err := latestPos.TrackPNLByTime(updated, last) - if err != nil { - return decimal.Zero, fmt.Errorf("%w for position %v %v %v", err, m.exchange, m.asset, m.pair) - } - latestPos.m.Lock() - defer latestPos.m.Unlock() - return latestPos.unrealisedPNL, nil -} - // GetStats returns a summary of a future position func (p *PositionTracker) GetStats() PositionStats { if p == nil { @@ -337,10 +351,6 @@ func (p *PositionTracker) GetStats() PositionStats { var orders []Detail orders = append(orders, p.longPositions...) orders = append(orders, p.shortPositions...) - rPNL := p.realisedPNL - if p.status == Closed { - rPNL = CalculateRealisedPNL(p.pnlHistory) - } return PositionStats{ Exchange: p.exchange, Asset: p.asset, @@ -348,15 +358,14 @@ func (p *PositionTracker) GetStats() PositionStats { Underlying: p.underlyingAsset, Status: p.status, Orders: orders, - RealisedPNL: rPNL, + RealisedPNL: p.realisedPNL, UnrealisedPNL: p.unrealisedPNL, - Direction: p.currentDirection, + LatestDirection: p.currentDirection, OpeningDirection: p.openingDirection, - EntryPrice: p.entryPrice, - EntryAmount: p.entryAmount, - Price: p.latestPrice, - Exposure: p.exposure, + OpeningPrice: p.entryPrice, + LatestPrice: p.latestPrice, PNLHistory: p.pnlHistory, + Exposure: p.exposure, } } @@ -364,7 +373,7 @@ func (p *PositionTracker) GetStats() PositionStats { // and current pricing. Adds the entry to PNL history to track over time func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) error { if p == nil { - return common.ErrNilPointer + return fmt.Errorf("position tracker %w", common.ErrNilPointer) } p.m.Lock() defer func() { @@ -373,24 +382,15 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro }() price := decimal.NewFromFloat(currentPrice) result := &PNLResult{ - Time: t, - Price: price, - UnrealisedPNL: p.unrealisedPNL, - RealisedPNLBeforeFees: p.realisedPNL, - Exposure: p.exposure, + Time: t, + Price: price, } - var diff decimal.Decimal if p.currentDirection.IsLong() { - diff = price.Sub(p.entryPrice) + diff := price.Sub(p.entryPrice) + result.UnrealisedPNL = p.exposure.Mul(diff) } else if p.currentDirection.IsShort() { - diff = p.entryPrice.Sub(price) - - } - var updatedUPNL = false - if !diff.IsZero() { - // only update if different + diff := p.entryPrice.Sub(price) result.UnrealisedPNL = p.exposure.Mul(diff) - updatedUPNL = true } if len(p.pnlHistory) > 0 { result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees @@ -398,9 +398,7 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro } var err error p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) - if updatedUPNL { - p.unrealisedPNL = result.UnrealisedPNL - } + p.unrealisedPNL = result.UnrealisedPNL return err } @@ -412,7 +410,7 @@ func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { } p.m.Lock() defer p.m.Unlock() - return CalculateRealisedPNL(p.pnlHistory) + return calculateRealisedPNL(p.pnlHistory) } // GetLatestPNLSnapshot takes the latest pnl history value @@ -428,7 +426,7 @@ func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { // futures contract func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p == nil { - return common.ErrNilPointer + return fmt.Errorf("position tracker %w", common.ErrNilPointer) } p.m.Lock() defer p.m.Unlock() @@ -593,11 +591,11 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } else { p.exposure = shortSide.Sub(longSide) } - log.Debugf(log.ExchangeSys, "order amount %v position exposure %v current direction %v order direction %v", d.Amount, p.exposure, p.currentDirection, d.Side) - if p.exposure.IsZero() { + + if p.exposure.Equal(decimal.Zero) { p.status = Closed p.closingPrice = decimal.NewFromFloat(d.Price) - p.realisedPNL = CalculateRealisedPNL(p.pnlHistory) + p.realisedPNL = calculateRealisedPNL(p.pnlHistory) p.unrealisedPNL = decimal.Zero } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { @@ -670,9 +668,9 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque return response, nil } -// CalculateRealisedPNL calculates the total realised PNL +// calculateRealisedPNL calculates the total realised PNL // based on PNL history, minus fees -func CalculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { +func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index ff1f7d4e057..274e403bd70 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -597,11 +597,11 @@ func TestClearPositionsForExchange(t *testing.T) { func TestCalculateRealisedPNL(t *testing.T) { t.Parallel() - result := CalculateRealisedPNL(nil) + result := calculateRealisedPNL(nil) if !result.IsZero() { t.Error("expected zero") } - result = CalculateRealisedPNL([]PNLResult{ + result = calculateRealisedPNL([]PNLResult{ { RealisedPNLBeforeFees: decimal.NewFromInt(1337), }, @@ -610,7 +610,7 @@ func TestCalculateRealisedPNL(t *testing.T) { t.Error("expected 1337") } - result = CalculateRealisedPNL([]PNLResult{ + result = calculateRealisedPNL([]PNLResult{ { RealisedPNLBeforeFees: decimal.NewFromInt(1339), Fee: decimal.NewFromInt(2), @@ -670,12 +670,12 @@ func TestSetupPositionTracker(t *testing.T) { Pair: cp, }) if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v", err, nil) + t.Fatalf("received '%v' expected '%v", err, nil) } - if p == nil { + if p == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Fatal("expected not nil") } - if p.exchange != testExchange { + if p.exchange != testExchange { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings t.Error("expected test") } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 8db769d9c54..3219dce7a4c 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -30,6 +30,8 @@ var ( ErrNotFuturesAsset = errors.New("asset type is not futures") // ErrUSDValueRequired returned when usd value unset ErrUSDValueRequired = errors.New("USD value required") + // ErrOfflineCalculationSet is raised when collateral calculation is set to be offline, yet is attempted online + ErrOfflineCalculationSet = errors.New("offline calculation set") // ErrNotFutureAsset ErrNotFutureAsset = errors.New("not a futures asset") @@ -57,26 +59,67 @@ type PNLCalculation interface { // on an exchange type CollateralManagement interface { GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) - ScaleCollateral(context.Context, string, *CollateralCalculator) (decimal.Decimal, error) - CalculateTotalCollateral(ctx context.Context, subAccount string, calculateOffline bool, collaterals []CollateralCalculator) (*TotalCollateralResponse, error) + ScaleCollateral(ctx context.Context, subAccount string, calculator *CollateralCalculator) (*CollateralByCurrency, error) + CalculateTotalCollateral(context.Context, *TotalCollateralCalculator) (*TotalCollateralResponse, error) } // TotalCollateralResponse holds all collateral type TotalCollateralResponse struct { - TotalCollateral decimal.Decimal - BreakdownByCurrency []CollateralByCurrency + CollateralCurrency currency.Code + TotalValueOfPositiveSpotBalances decimal.Decimal + CollateralContributedByPositiveSpotBalances decimal.Decimal + UsedCollateral decimal.Decimal + UsedBreakdown *UsedCollateralBreakdown + AvailableCollateral decimal.Decimal + AvailableMaintenanceCollateral decimal.Decimal + UnrealisedPNL decimal.Decimal + BreakdownByCurrency []CollateralByCurrency + BreakdownOfPositions []CollateralByPosition +} + +// CollateralByPosition shows how much collateral is used +// from positions +type CollateralByPosition struct { + PositionCurrency currency.Pair + Size decimal.Decimal + OpenOrderSize decimal.Decimal + PositionSize decimal.Decimal + MarkPrice decimal.Decimal + RequiredMargin decimal.Decimal + CollateralUsed decimal.Decimal } // CollateralByCurrency individual collateral contribution // along with what the potentially scaled collateral // currency it is represented as -// eg in FTX ValueCurrency is USD +// eg in FTX ScaledCurrency is USD type CollateralByCurrency struct { - Currency currency.Code - OriginalValue decimal.Decimal - ScaledValue decimal.Decimal - ValueCurrency currency.Code - Error error + Currency currency.Code + SkipContribution bool + TotalFunds decimal.Decimal + AvailableForUseAsCollateral decimal.Decimal + CollateralContribution decimal.Decimal + AdditionalCollateralUsed decimal.Decimal + FairMarketValue decimal.Decimal + Weighting decimal.Decimal + ScaledCurrency currency.Code + UnrealisedPNL decimal.Decimal + ScaledUsed decimal.Decimal + ScaledUsedBreakdown *UsedCollateralBreakdown + Error error +} + +// UsedCollateralBreakdown provides a detailed +// breakdown of where collateral is currently being allocated +type UsedCollateralBreakdown struct { + LockedInStakes decimal.Decimal + LockedInNFTBids decimal.Decimal + LockedInFeeVoucher decimal.Decimal + LockedInSpotMarginFundingOffers decimal.Decimal + LockedInSpotOrders decimal.Decimal + LockedAsCollateral decimal.Decimal + UsedInPositions decimal.Decimal + UsedInSpotMarginBorrows decimal.Decimal } // PositionController manages all futures orders @@ -136,7 +179,6 @@ type PositionTracker struct { exposure decimal.Decimal currentDirection Side openingDirection Side - entryAmount decimal.Decimal status Status unrealisedPNL decimal.Decimal realisedPNL decimal.Decimal @@ -156,13 +198,21 @@ type PositionTracker struct { type PositionTrackerSetup struct { Pair currency.Pair EntryPrice decimal.Decimal - EntryAmount decimal.Decimal Underlying currency.Code Asset asset.Item Side Side UseExchangePNLCalculation bool } +// TotalCollateralCalculator holds many collateral calculators +// to calculate total collateral standing with one struct +type TotalCollateralCalculator struct { + SubAccount string + CollateralAssets []CollateralCalculator + CalculateOffline bool + FetchPositions bool +} + // CollateralCalculator is used to determine // the size of collateral holdings for an exchange // eg on FTX, the collateral is scaled depending on what @@ -172,9 +222,12 @@ type CollateralCalculator struct { CollateralCurrency currency.Code Asset asset.Item Side Side - CollateralAmount decimal.Decimal - CollateralPrice decimal.Decimal + USDPrice decimal.Decimal IsLiquidating bool + IsForNewPosition bool + FreeCollateral decimal.Decimal + LockedCollateral decimal.Decimal + UnrealisedPNL decimal.Decimal } // PNLCalculator implements the PNLCalculation interface @@ -219,21 +272,18 @@ type PNLResult struct { // PositionStats is a basic holder // for position information type PositionStats struct { - Exchange string - Asset asset.Item - Pair currency.Pair - Underlying currency.Code - Orders []Detail - RealisedPNL decimal.Decimal - UnrealisedPNL decimal.Decimal - Status Status - Direction Side - Price decimal.Decimal - Exposure decimal.Decimal - + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + Orders []Detail + RealisedPNL decimal.Decimal + UnrealisedPNL decimal.Decimal + Exposure decimal.Decimal + LatestDirection Side + Status Status OpeningDirection Side - EntryAmount decimal.Decimal - EntryPrice decimal.Decimal - - PNLHistory []PNLResult + OpeningPrice decimal.Decimal + LatestPrice decimal.Decimal + PNLHistory []PNLResult } diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index 76e587eb3b5..b7ef4dfe809 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -374,7 +374,7 @@ func TestFilterOrdersByCurrencies(t *testing.T) { if len(orders) != 1 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) } - currencies = append(currencies, currency.Pair{}) + currencies = append(currencies, currency.EMPTYPAIR) FilterOrdersByCurrencies(&orders, currencies) if len(orders) != 1 { t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders)) @@ -718,7 +718,7 @@ func TestUpdateOrderFromModify(t *testing.T) { AssetType: "", Date: time.Time{}, LastUpdated: time.Time{}, - Pair: currency.Pair{}, + Pair: currency.EMPTYPAIR, Trades: nil, } updated := time.Now() @@ -910,7 +910,7 @@ func TestUpdateOrderFromDetail(t *testing.T) { AssetType: "", Date: time.Time{}, LastUpdated: time.Time{}, - Pair: currency.Pair{}, + Pair: currency.EMPTYPAIR, Trades: nil, } updated := time.Now() diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index c1dc40ffefc..39aaf14f2c9 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -114,7 +114,9 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) { d.PostOnly = m.PostOnly updated = true } - if !m.Pair.IsEmpty() && m.Pair != d.Pair { + if !m.Pair.IsEmpty() && !m.Pair.Equal(d.Pair) { + // TODO: Add a check to see if the original pair is empty as well, but + // error if it is changing from BTC-USD -> LTC-USD. d.Pair = m.Pair updated = true } @@ -276,7 +278,9 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) { d.PostOnly = m.PostOnly updated = true } - if !m.Pair.IsEmpty() && m.Pair != d.Pair { + if !m.Pair.IsEmpty() && !m.Pair.Equal(d.Pair) { + // TODO: Add a check to see if the original pair is empty as well, but + // error if it is changing from BTC-USD -> LTC-USD. d.Pair = m.Pair updated = true } diff --git a/exchanges/orderbook/orderbook_test.go b/exchanges/orderbook/orderbook_test.go index 2387f659bfe..46b4a0a9709 100644 --- a/exchanges/orderbook/orderbook_test.go +++ b/exchanges/orderbook/orderbook_test.go @@ -303,7 +303,7 @@ func TestDeployDepth(t *testing.T) { if !errors.Is(err, errExchangeNameUnset) { t.Fatalf("expecting %s error but received %v", errExchangeNameUnset, err) } - _, err = DeployDepth("test", currency.Pair{}, asset.Spot) + _, err = DeployDepth("test", currency.EMPTYPAIR, asset.Spot) if !errors.Is(err, errPairNotSet) { t.Fatalf("expecting %s error but received %v", errPairNotSet, err) } @@ -370,7 +370,7 @@ func TestProcessOrderbook(t *testing.T) { } // test for empty pair - base.Pair = currency.Pair{} + base.Pair = currency.EMPTYPAIR err = base.Process() if err == nil { t.Error("empty pair should throw an err") diff --git a/exchanges/poloniex/currency_details.go b/exchanges/poloniex/currency_details.go index 42aedeba40b..71e7b50fe43 100644 --- a/exchanges/poloniex/currency_details.go +++ b/exchanges/poloniex/currency_details.go @@ -101,7 +101,7 @@ func (w *CurrencyDetails) GetPair(id float64) (currency.Pair, error) { w.m.RLock() defer w.m.RUnlock() if w.pairs == nil { - return currency.Pair{}, errPairMapIsNil + return currency.EMPTYPAIR, errPairMapIsNil } p, ok := w.pairs[id] @@ -124,13 +124,13 @@ func (w *CurrencyDetails) GetCode(id float64) (currency.Code, error) { w.m.RLock() defer w.m.RUnlock() if w.codes == nil { - return currency.Code{}, errCodeMapIsNil + return currency.EMPTYCODE, errCodeMapIsNil } c, ok := w.codes[id] if ok { return c.Currency, nil } - return currency.Code{}, errIDNotFoundInCodeMap + return currency.EMPTYCODE, errIDNotFoundInCodeMap } // GetWithdrawalTXFee returns withdrawal transaction fee for the currency diff --git a/exchanges/poloniex/currency_details_test.go b/exchanges/poloniex/currency_details_test.go index d5c6858432a..79989e7c48d 100644 --- a/exchanges/poloniex/currency_details_test.go +++ b/exchanges/poloniex/currency_details_test.go @@ -35,32 +35,32 @@ func TestWsCurrencyMap(t *testing.T) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } - _, err = m.GetWithdrawalTXFee(currency.Code{}) + _, err = m.GetWithdrawalTXFee(currency.EMPTYCODE) if !errors.Is(err, errCodeMapIsNil) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } - _, err = m.GetDepositAddress(currency.Code{}) + _, err = m.GetDepositAddress(currency.EMPTYCODE) if !errors.Is(err, errCodeMapIsNil) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } - _, err = m.IsWithdrawAndDepositsEnabled(currency.Code{}) + _, err = m.IsWithdrawAndDepositsEnabled(currency.EMPTYCODE) if !errors.Is(err, errCodeMapIsNil) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } - _, err = m.IsTradingEnabledForCurrency(currency.Code{}) + _, err = m.IsTradingEnabledForCurrency(currency.EMPTYCODE) if !errors.Is(err, errCodeMapIsNil) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } - _, err = m.IsTradingEnabledForPair(currency.Pair{}) + _, err = m.IsTradingEnabledForPair(currency.EMPTYPAIR) if !errors.Is(err, errCodeMapIsNil) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } - _, err = m.IsPostOnlyForPair(currency.Pair{}) + _, err = m.IsPostOnlyForPair(currency.EMPTYPAIR) if !errors.Is(err, errCodeMapIsNil) { t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err) } @@ -178,32 +178,32 @@ func TestWsCurrencyMap(t *testing.T) { t.Fatal("unexpected results") } - _, err = m.GetWithdrawalTXFee(currency.Code{}) + _, err = m.GetWithdrawalTXFee(currency.EMPTYCODE) if !errors.Is(err, errCurrencyNotFoundInMap) { t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err) } - _, err = m.GetDepositAddress(currency.Code{}) + _, err = m.GetDepositAddress(currency.EMPTYCODE) if !errors.Is(err, errCurrencyNotFoundInMap) { t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err) } - _, err = m.IsWithdrawAndDepositsEnabled(currency.Code{}) + _, err = m.IsWithdrawAndDepositsEnabled(currency.EMPTYCODE) if !errors.Is(err, errCurrencyNotFoundInMap) { t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err) } - _, err = m.IsTradingEnabledForCurrency(currency.Code{}) + _, err = m.IsTradingEnabledForCurrency(currency.EMPTYCODE) if !errors.Is(err, errCurrencyNotFoundInMap) { t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err) } - _, err = m.IsTradingEnabledForPair(currency.Pair{}) + _, err = m.IsTradingEnabledForPair(currency.EMPTYPAIR) if !errors.Is(err, errCurrencyNotFoundInMap) { t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err) } - _, err = m.IsPostOnlyForPair(currency.Pair{}) + _, err = m.IsPostOnlyForPair(currency.EMPTYPAIR) if !errors.Is(err, errCurrencyNotFoundInMap) { t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err) } diff --git a/exchanges/poloniex/poloniex_mock_test.go b/exchanges/poloniex/poloniex_mock_test.go index c050773176a..8f1af4db72c 100644 --- a/exchanges/poloniex/poloniex_mock_test.go +++ b/exchanges/poloniex/poloniex_mock_test.go @@ -45,7 +45,10 @@ func TestMain(m *testing.M) { log.Fatalf("Mock server error %s", err) } - p.HTTPClient = newClient + err = p.SetHTTPClient(newClient) + if err != nil { + log.Fatalf("Mock server error %s", err) + } endpoints := p.API.Endpoints.GetURLMap() for k := range endpoints { err = p.API.Endpoints.SetRunning(k, serverDetails) diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index ed675308530..2a652790c4f 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -133,9 +133,12 @@ func (p *Poloniex) SetDefaults() { }, } - p.Requester = request.New(p.Name, + p.Requester, err = request.New(p.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } p.API.Endpoints = p.NewEndpoints() err = p.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: poloniexAPIURL, @@ -408,10 +411,10 @@ func (p *Poloniex) UpdateAccountInfo(ctx context.Context, assetType asset.Item) var currencies []account.Balance for x, y := range accountBalance.Currency { - var exchangeCurrency account.Balance - exchangeCurrency.CurrencyName = currency.NewCode(x) - exchangeCurrency.TotalValue = y - currencies = append(currencies, exchangeCurrency) + currencies = append(currencies, account.Balance{ + CurrencyName: currency.NewCode(x), + Total: y, + }) } response.Accounts = append(response.Accounts, account.SubAccount{ diff --git a/exchanges/request/client.go b/exchanges/request/client.go new file mode 100644 index 00000000000..536472814fe --- /dev/null +++ b/exchanges/request/client.go @@ -0,0 +1,131 @@ +package request + +import ( + "errors" + "net/http" + "net/url" + "sync" + "time" +) + +var ( + // tracker is the global to maintain sanity between clients across all + // services using the request package. + tracker clientTracker + + errNoProxyURLSupplied = errors.New("no proxy URL supplied") + errCannotReuseHTTPClient = errors.New("cannot reuse http client") + errHTTPClientIsNil = errors.New("http client is nil") + errHTTPClientNotFound = errors.New("http client not found") +) + +// clientTracker attempts to maintain service/http.Client segregation +type clientTracker struct { + clients []*http.Client + sync.Mutex +} + +// checkAndRegister stops the sharing of the same http.Client between services. +func (c *clientTracker) checkAndRegister(newClient *http.Client) error { + if newClient == nil { + return errHTTPClientIsNil + } + c.Lock() + defer c.Unlock() + for x := range c.clients { + if newClient == c.clients[x] { + return errCannotReuseHTTPClient + } + } + c.clients = append(c.clients, newClient) + return nil +} + +// deRegister removes the *http.Client from being tracked +func (c *clientTracker) deRegister(oldClient *http.Client) error { + if oldClient == nil { + return errHTTPClientIsNil + } + c.Lock() + defer c.Unlock() + for x := range c.clients { + if oldClient != c.clients[x] { + continue + } + c.clients[x] = c.clients[len(c.clients)-1] + c.clients[len(c.clients)-1] = nil + c.clients = c.clients[:len(c.clients)-1] + return nil + } + return errHTTPClientNotFound +} + +// client wraps over a http client for better protection +type client struct { + protected *http.Client + m sync.RWMutex +} + +// newProtectedClient registers a http.Client to inhibit cross service usage and +// return a thread safe holder (*request.Client) with getter and setters for +// timeouts and transports. +func newProtectedClient(newClient *http.Client) (*client, error) { + if err := tracker.checkAndRegister(newClient); err != nil { + return nil, err + } + return &client{protected: newClient}, nil +} + +// setProxy sets a proxy address for the client transport +func (c *client) setProxy(p *url.URL) error { + if p == nil || p.String() == "" { + return errNoProxyURLSupplied + } + c.m.Lock() + defer c.m.Unlock() + // Check transport first so we don't set something and then error. + tr, ok := c.protected.Transport.(*http.Transport) + if !ok { + return errTransportNotSet + } + // This closes idle connections before an attempt at reassignment and + // boots any dangly routines. + tr.CloseIdleConnections() + tr.Proxy = http.ProxyURL(p) + tr.TLSHandshakeTimeout = proxyTLSTimeout + return nil +} + +// setHTTPClientTimeout sets the timeout value for the exchanges HTTP Client and +// also the underlying transports idle connection timeout +func (c *client) setHTTPClientTimeout(timeout time.Duration) error { + c.m.Lock() + defer c.m.Unlock() + // Check transport first so we don't set something and then error. + tr, ok := c.protected.Transport.(*http.Transport) + if !ok { + return errTransportNotSet + } + // This closes idle connections before an attempt at reassignment and + // boots any dangly routines. + tr.CloseIdleConnections() + tr.IdleConnTimeout = timeout + c.protected.Timeout = timeout + return nil +} + +// do sends request in a protected manner +func (c *client) do(request *http.Request) (resp *http.Response, err error) { + c.m.RLock() + resp, err = c.protected.Do(request) + c.m.RUnlock() + return +} + +// release de-registers the underlying client +func (c *client) release() error { + c.m.Lock() + err := tracker.deRegister(c.protected) + c.m.Unlock() + return err +} diff --git a/exchanges/request/client_test.go b/exchanges/request/client_test.go new file mode 100644 index 00000000000..a68f7f8742a --- /dev/null +++ b/exchanges/request/client_test.go @@ -0,0 +1,148 @@ +package request + +import ( + "errors" + "net/http" + "net/url" + "testing" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" +) + +// this doesn't need to be included in binary +func (c *clientTracker) contains(check *http.Client) bool { + c.Lock() + defer c.Unlock() + for x := range c.clients { + if check == c.clients[x] { + return true + } + } + return false +} + +func TestCheckAndRegister(t *testing.T) { + t.Parallel() + err := tracker.checkAndRegister(nil) + if !errors.Is(err, errHTTPClientIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errHTTPClientIsNil) + } + + newLovelyClient := new(http.Client) + err = tracker.checkAndRegister(newLovelyClient) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if !tracker.contains(newLovelyClient) { + t.Fatalf("received: '%v' but expected: '%v'", false, true) + } + + err = tracker.checkAndRegister(newLovelyClient) + if !errors.Is(err, errCannotReuseHTTPClient) { + t.Fatalf("received: '%v' but expected: '%v'", err, errCannotReuseHTTPClient) + } +} + +func TestDeRegister(t *testing.T) { + t.Parallel() + err := tracker.deRegister(nil) + if !errors.Is(err, errHTTPClientIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errHTTPClientIsNil) + } + + newLovelyClient := new(http.Client) + err = tracker.deRegister(newLovelyClient) + if !errors.Is(err, errHTTPClientNotFound) { + t.Fatalf("received: '%v' but expected: '%v'", err, errHTTPClientNotFound) + } + + err = tracker.checkAndRegister(newLovelyClient) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if !tracker.contains(newLovelyClient) { + t.Fatalf("received: '%v' but expected: '%v'", false, true) + } + + err = tracker.deRegister(newLovelyClient) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if tracker.contains(newLovelyClient) { + t.Fatalf("received: '%v' but expected: '%v'", true, false) + } +} + +func TestNewProtectedClient(t *testing.T) { + t.Parallel() + if _, err := newProtectedClient(nil); !errors.Is(err, errHTTPClientIsNil) { + t.Fatalf("received: '%v' but expected: '%v'", err, errHTTPClientIsNil) + } + + newLovelyClient := new(http.Client) + protec, err := newProtectedClient(newLovelyClient) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if protec.protected != newLovelyClient { + t.Fatal("unexpected value") + } +} + +func TestClientSetProxy(t *testing.T) { + t.Parallel() + err := (&client{}).setProxy(nil) + if !errors.Is(err, errNoProxyURLSupplied) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoProxyURLSupplied) + } + pp, err := url.Parse("lol.com") + if err != nil { + t.Fatal(err) + } + err = (&client{protected: new(http.Client)}).setProxy(pp) + if !errors.Is(err, errTransportNotSet) { + t.Fatalf("received: '%v' but expected: '%v'", err, errTransportNotSet) + } + err = (&client{protected: common.NewHTTPClientWithTimeout(0)}).setProxy(pp) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestClientSetHTTPClientTimeout(t *testing.T) { + t.Parallel() + err := (&client{protected: new(http.Client)}).setHTTPClientTimeout(time.Second) + if !errors.Is(err, errTransportNotSet) { + t.Fatalf("received: '%v' but expected: '%v'", err, errTransportNotSet) + } + err = (&client{protected: common.NewHTTPClientWithTimeout(0)}).setHTTPClientTimeout(time.Second) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestRelease(t *testing.T) { + t.Parallel() + newLovelyClient, err := newProtectedClient(common.NewHTTPClientWithTimeout(0)) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if !tracker.contains(newLovelyClient.protected) { + t.Fatalf("received: '%v' but expected: '%v'", false, true) + } + + err = newLovelyClient.release() + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if tracker.contains(newLovelyClient.protected) { + t.Fatalf("received: '%v' but expected: '%v'", true, false) + } +} diff --git a/exchanges/request/limit.go b/exchanges/request/limit.go index 8ff7a3f7c3c..e2179399fbc 100644 --- a/exchanges/request/limit.go +++ b/exchanges/request/limit.go @@ -3,12 +3,19 @@ package request import ( "context" "errors" + "fmt" "sync/atomic" "time" "golang.org/x/time/rate" ) +// Defines rate limiting errors +var ( + ErrRateLimiterAlreadyDisabled = errors.New("rate limiter already disabled") + ErrRateLimiterAlreadyEnabled = errors.New("rate limiter already enabled") +) + // Const here define individual functionality sub types for rate limiting const ( Unset EndpointLimit = iota @@ -60,6 +67,9 @@ func NewBasicRateLimit(interval time.Duration, actions int) Limiter { // InitiateRateLimit sleeps for designated end point rate limits func (r *Requester) InitiateRateLimit(ctx context.Context, e EndpointLimit) error { + if r == nil { + return ErrRequestSystemIsNil + } if atomic.LoadInt32(&r.disableRateLimiter) == 1 { return nil } @@ -73,16 +83,22 @@ func (r *Requester) InitiateRateLimit(ctx context.Context, e EndpointLimit) erro // DisableRateLimiter disables the rate limiting system for the exchange func (r *Requester) DisableRateLimiter() error { + if r == nil { + return ErrRequestSystemIsNil + } if !atomic.CompareAndSwapInt32(&r.disableRateLimiter, 0, 1) { - return errors.New("rate limiter already disabled") + return fmt.Errorf("%s %w", r.name, ErrRateLimiterAlreadyDisabled) } return nil } // EnableRateLimiter enables the rate limiting system for the exchange func (r *Requester) EnableRateLimiter() error { + if r == nil { + return ErrRequestSystemIsNil + } if !atomic.CompareAndSwapInt32(&r.disableRateLimiter, 1, 0) { - return errors.New("rate limiter already enabled") + return fmt.Errorf("%s %w", r.name, ErrRateLimiterAlreadyEnabled) } return nil } diff --git a/exchanges/request/request.go b/exchanges/request/request.go index 15c779381d6..99b678cbf74 100644 --- a/exchanges/request/request.go +++ b/exchanges/request/request.go @@ -20,7 +20,9 @@ import ( ) var ( - errRequestSystemIsNil = errors.New("request system is nil") + // ErrRequestSystemIsNil defines and error if the request system has not + // been set up yet. + ErrRequestSystemIsNil = errors.New("request system is nil") errMaxRequestJobs = errors.New("max request jobs reached") errRequestFunctionIsNil = errors.New("request function is nil") errRequestItemNil = errors.New("request item is nil") @@ -28,13 +30,18 @@ var ( errHeaderResponseMapIsNil = errors.New("header response map is nil") errFailedToRetryRequest = errors.New("failed to retry request") errContextRequired = errors.New("context is required") + errTransportNotSet = errors.New("transport not set, cannot set timeout") ) // New returns a new Requester -func New(name string, httpRequester *http.Client, opts ...RequesterOption) *Requester { +func New(name string, httpRequester *http.Client, opts ...RequesterOption) (*Requester, error) { + protectedClient, err := newProtectedClient(httpRequester) + if err != nil { + return nil, fmt.Errorf("cannot set up a new requester for %s: %w", name, err) + } r := &Requester{ - HTTPClient: httpRequester, - Name: name, + _HTTPClient: protectedClient, + name: name, backoff: DefaultBackoff(), retryPolicy: DefaultRetryPolicy, maxRetries: MaxRetryAttempts, @@ -46,13 +53,13 @@ func New(name string, httpRequester *http.Client, opts ...RequesterOption) *Requ o(r) } - return r + return r, nil } // SendPayload handles sending HTTP/HTTPS requests func (r *Requester) SendPayload(ctx context.Context, ep EndpointLimit, newRequest Generate) error { if r == nil { - return errRequestSystemIsNil + return ErrRequestSystemIsNil } if ctx == nil { @@ -107,8 +114,8 @@ func (i *Item) validateRequest(ctx context.Context, r *Requester) (*http.Request req.Header.Add(k, v) } - if r.UserAgent != "" && req.Header.Get(userAgent) == "" { - req.Header.Add(userAgent, r.UserAgent) + if r.userAgent != "" && req.Header.Get(userAgent) == "" { + req.Header.Add(userAgent, r.userAgent) } return req, nil @@ -141,22 +148,22 @@ func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRe } if p.Verbose { - log.Debugf(log.RequestSys, "%s attempt %d request path: %s", r.Name, attempt, p.Path) + log.Debugf(log.RequestSys, "%s attempt %d request path: %s", r.name, attempt, p.Path) for k, d := range req.Header { - log.Debugf(log.RequestSys, "%s request header [%s]: %s", r.Name, k, d) + log.Debugf(log.RequestSys, "%s request header [%s]: %s", r.name, k, d) } - log.Debugf(log.RequestSys, "%s request type: %s", r.Name, p.Method) + log.Debugf(log.RequestSys, "%s request type: %s", r.name, p.Method) if p.Body != nil { - log.Debugf(log.RequestSys, "%s request body: %v", r.Name, p.Body) + log.Debugf(log.RequestSys, "%s request body: %v", r.name, p.Body) } } start := time.Now() - resp, err := r.HTTPClient.Do(req) + resp, err := r._HTTPClient.do(req) if r.reporter != nil { - r.reporter.Latency(r.Name, p.Method, p.Path, time.Since(start)) + r.reporter.Latency(r.name, p.Method, p.Path, time.Since(start)) } if retry, checkErr := r.retryPolicy(resp, err); checkErr != nil { @@ -191,7 +198,7 @@ func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRe if p.Verbose { log.Errorf(log.RequestSys, "%s request has failed. Retrying request in %s, attempt %d", - r.Name, + r.name, delay, attempt) } @@ -213,7 +220,7 @@ func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRe if p.HTTPRecording { // This dumps http responses for future mocking implementations - err = mock.HTTPRecord(resp, r.Name, contents) + err = mock.HTTPRecord(resp, r.name, contents) if err != nil { return fmt.Errorf("mock recording failure %s", err) } @@ -228,21 +235,27 @@ func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRe if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusAccepted { return fmt.Errorf("%s unsuccessful HTTP status code: %d raw response: %s", - r.Name, + r.name, resp.StatusCode, string(contents)) } if p.HTTPDebugging { - dump, err := httputil.DumpResponse(resp, false) + dump, dumpErr := httputil.DumpResponse(resp, false) if err != nil { - log.Errorf(log.RequestSys, "DumpResponse invalid response: %v:", err) + log.Errorf(log.RequestSys, "DumpResponse invalid response: %v:", dumpErr) } log.Debugf(log.RequestSys, "DumpResponse Headers (%v):\n%s", p.Path, dump) log.Debugf(log.RequestSys, "DumpResponse Body (%v):\n %s", p.Path, string(contents)) } - resp.Body.Close() + err = resp.Body.Close() + if err != nil { + log.Errorf(log.RequestSys, + "%s failed to close request body %s", + r.name, + err) + } if p.Verbose { log.Debugf(log.RequestSys, "HTTP status: %s, Code: %v", @@ -251,7 +264,7 @@ func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRe if !p.HTTPDebugging { log.Debugf(log.RequestSys, "%s raw response: %s", - r.Name, + r.name, string(contents)) } } @@ -259,6 +272,22 @@ func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRe } } +func (r *Requester) drainBody(body io.ReadCloser) { + if _, err := io.Copy(ioutil.Discard, io.LimitReader(body, drainBodyLimit)); err != nil { + log.Errorf(log.RequestSys, + "%s failed to drain request body %s", + r.name, + err) + } + + if err := body.Close(); err != nil { + log.Errorf(log.RequestSys, + "%s failed to close request body %s", + r.name, + err) + } +} + // GetNonce returns a nonce for requests. This locks and enforces concurrent // nonce FIFO on the buffered job channel func (r *Requester) GetNonce(isNano bool) nonce.Value { @@ -285,27 +314,57 @@ func (r *Requester) GetNonceMilli() nonce.Value { return r.Nonce.GetInc() } -// SetProxy sets a proxy address to the client transport +// SetProxy sets a proxy address for the client transport func (r *Requester) SetProxy(p *url.URL) error { - if p.String() == "" { - return errors.New("no proxy URL supplied") + if r == nil { + return ErrRequestSystemIsNil + } + return r._HTTPClient.setProxy(p) +} + +// SetHTTPClient sets exchanges HTTP client +func (r *Requester) SetHTTPClient(newClient *http.Client) error { + if r == nil { + return ErrRequestSystemIsNil + } + protectedClient, err := newProtectedClient(newClient) + if err != nil { + return err + } + r._HTTPClient = protectedClient + return nil +} + +// SetClientTimeout sets the timeout value for the exchanges HTTP Client and +// also the underlying transports idle connection timeout +func (r *Requester) SetHTTPClientTimeout(timeout time.Duration) error { + if r == nil { + return ErrRequestSystemIsNil } + return r._HTTPClient.setHTTPClientTimeout(timeout) +} - t, ok := r.HTTPClient.Transport.(*http.Transport) - if !ok { - return errors.New("transport not set, cannot set proxy") +// SetHTTPClientUserAgent sets the exchanges HTTP user agent +func (r *Requester) SetHTTPClientUserAgent(userAgent string) error { + if r == nil { + return ErrRequestSystemIsNil } - t.Proxy = http.ProxyURL(p) - t.TLSHandshakeTimeout = proxyTLSTimeout + r.userAgent = userAgent return nil } -func (r *Requester) drainBody(body io.ReadCloser) { - defer body.Close() - if _, err := io.Copy(ioutil.Discard, io.LimitReader(body, drainBodyLimit)); err != nil { - log.Errorf(log.RequestSys, - "%s failed to drain request body %s", - r.Name, - err) +// GetHTTPClientUserAgent gets the exchanges HTTP user agent +func (r *Requester) GetHTTPClientUserAgent() (string, error) { + if r == nil { + return "", ErrRequestSystemIsNil + } + return r.userAgent, nil +} + +// Shutdown releases persistent memory for garbage collection. +func (r *Requester) Shutdown() error { + if r == nil { + return ErrRequestSystemIsNil } + return r._HTTPClient.release() } diff --git a/exchanges/request/request_test.go b/exchanges/request/request_test.go index e4d0dbd79e1..8eccdf3ee81 100644 --- a/exchanges/request/request_test.go +++ b/exchanges/request/request_test.go @@ -18,6 +18,7 @@ import ( "testing" "time" + "github.com/thrasher-corp/gocryptotrader/common" "golang.org/x/time/rate" ) @@ -126,12 +127,15 @@ func TestNewRateLimit(t *testing.T) { func TestCheckRequest(t *testing.T) { t.Parallel() - r := New("TestRequest", + r, err := New("TestRequest", new(http.Client)) + if err != nil { + t.Fatal(err) + } ctx := context.Background() var check *Item - _, err := check.validateRequest(ctx, &Requester{}) + _, err = check.validateRequest(ctx, &Requester{}) if err == nil { t.Fatal(unexpected) } @@ -179,7 +183,7 @@ func TestCheckRequest(t *testing.T) { } // Test user agent set - r.UserAgent = "r00t axxs" + r.userAgent = "r00t axxs" req, err := check.validateRequest(ctx, r) if err != nil { t.Fatal(err) @@ -226,12 +230,15 @@ var globalshell = GlobalLimitTest{ func TestDoRequest(t *testing.T) { t.Parallel() - r := New("test", new(http.Client), WithLimiter(&globalshell)) + r, err := New("test", new(http.Client), WithLimiter(&globalshell)) + if err != nil { + t.Fatal(err) + } ctx := context.Background() - err := (*Requester)(nil).SendPayload(ctx, Unset, nil) - if !errors.Is(errRequestSystemIsNil, err) { - t.Fatalf("expected: %v but received: %v", errRequestSystemIsNil, err) + err = (*Requester)(nil).SendPayload(ctx, Unset, nil) + if !errors.Is(ErrRequestSystemIsNil, err) { + t.Fatalf("expected: %v but received: %v", ErrRequestSystemIsNil, err) } err = r.SendPayload(ctx, Unset, nil) if !errors.Is(errRequestFunctionIsNil, err) { @@ -305,8 +312,16 @@ func TestDoRequest(t *testing.T) { // reset jobs r.jobs = 0 + r._HTTPClient, err = newProtectedClient(common.NewHTTPClientWithTimeout(0)) + if err != nil { + t.Fatal(err) + } + // timeout checker - r.HTTPClient.Timeout = time.Millisecond * 50 + err = r._HTTPClient.setHTTPClientTimeout(time.Millisecond * 50) + if err != nil { + t.Fatal(err) + } err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return &Item{Path: testURL + "/timeout"}, nil }) @@ -314,7 +329,10 @@ func TestDoRequest(t *testing.T) { t.Fatalf("received: %v but expected: %v", err, errFailedToRetryRequest) } // reset timeout - r.HTTPClient.Timeout = 0 + err = r._HTTPClient.setHTTPClientTimeout(0) + if err != nil { + t.Fatal(err) + } // Check JSON var resp struct { @@ -403,7 +421,10 @@ func TestDoRequest_Retries(t *testing.T) { backoff := func(n int) time.Duration { return 0 } - r := New("test", new(http.Client), WithBackoff(backoff)) + r, err := New("test", new(http.Client), WithBackoff(backoff)) + if err != nil { + t.Fatal(err) + } var failed int32 var wg sync.WaitGroup wg.Add(4) @@ -444,8 +465,11 @@ func TestDoRequest_RetryNonRecoverable(t *testing.T) { backoff := func(n int) time.Duration { return 0 } - r := New("test", new(http.Client), WithBackoff(backoff)) - err := r.SendPayload(context.Background(), Unset, func() (*Item, error) { + r, err := New("test", new(http.Client), WithBackoff(backoff)) + if err != nil { + t.Fatal(err) + } + err = r.SendPayload(context.Background(), Unset, func() (*Item, error) { return &Item{ Method: http.MethodGet, Path: testURL + "/always-retry", @@ -466,8 +490,11 @@ func TestDoRequest_NotRetryable(t *testing.T) { backoff := func(n int) time.Duration { return time.Duration(n) * time.Millisecond } - r := New("test", new(http.Client), WithRetryPolicy(retry), WithBackoff(backoff)) - err := r.SendPayload(context.Background(), Unset, func() (*Item, error) { + r, err := New("test", new(http.Client), WithRetryPolicy(retry), WithBackoff(backoff)) + if err != nil { + t.Fatal(err) + } + err = r.SendPayload(context.Background(), Unset, func() (*Item, error) { return &Item{ Method: http.MethodGet, Path: testURL + "/always-retry", @@ -480,17 +507,22 @@ func TestDoRequest_NotRetryable(t *testing.T) { func TestGetNonce(t *testing.T) { t.Parallel() - r := New("test", + r, err := New("test", new(http.Client), WithLimiter(&globalshell)) - + if err != nil { + t.Fatal(err) + } if n1, n2 := r.GetNonce(false), r.GetNonce(false); n1 == n2 { t.Fatal(unexpected) } - r2 := New("test", + r2, err := New("test", new(http.Client), WithLimiter(&globalshell)) + if err != nil { + t.Fatal(err) + } if n1, n2 := r2.GetNonce(true), r2.GetNonce(true); n1 == n2 { t.Fatal(unexpected) } @@ -498,9 +530,12 @@ func TestGetNonce(t *testing.T) { func TestGetNonceMillis(t *testing.T) { t.Parallel() - r := New("test", + r, err := New("test", new(http.Client), WithLimiter(&globalshell)) + if err != nil { + t.Fatal(err) + } if m1, m2 := r.GetNonceMilli(), r.GetNonceMilli(); m1 == m2 { log.Fatal(unexpected) } @@ -508,9 +543,17 @@ func TestGetNonceMillis(t *testing.T) { func TestSetProxy(t *testing.T) { t.Parallel() - r := New("test", + var r *Requester + err := r.SetProxy(nil) + if !errors.Is(err, ErrRequestSystemIsNil) { + t.Fatalf("received: '%v', but expected: '%v'", err, ErrRequestSystemIsNil) + } + r, err = New("test", &http.Client{Transport: new(http.Transport)}, WithLimiter(&globalshell)) + if err != nil { + t.Fatal(err) + } u, err := url.Parse("http://www.google.com") if err != nil { t.Fatal(err) @@ -530,9 +573,12 @@ func TestSetProxy(t *testing.T) { } func TestBasicLimiter(t *testing.T) { - r := New("test", + r, err := New("test", new(http.Client), WithLimiter(NewBasicRateLimit(time.Second, 1))) + if err != nil { + t.Fatal(err) + } i := Item{ Path: "http://www.google.com", Method: http.MethodGet, @@ -540,7 +586,7 @@ func TestBasicLimiter(t *testing.T) { ctx := context.Background() tn := time.Now() - err := r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil }) + err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil }) if err != nil { t.Fatal(err) } @@ -561,13 +607,16 @@ func TestBasicLimiter(t *testing.T) { } func TestEnableDisableRateLimit(t *testing.T) { - r := New("TestRequest", + r, err := New("TestRequest", new(http.Client), WithLimiter(NewBasicRateLimit(time.Minute, 1))) + if err != nil { + t.Fatal(err) + } ctx := context.Background() var resp interface{} - err := r.SendPayload(ctx, Auth, func() (*Item, error) { + err = r.SendPayload(ctx, Auth, func() (*Item, error) { return &Item{ Method: http.MethodGet, Path: testURL, @@ -635,3 +684,71 @@ func TestEnableDisableRateLimit(t *testing.T) { // Correct test } } + +func TestSetHTTPClient(t *testing.T) { + var r *Requester + err := r.SetHTTPClient(nil) + if !errors.Is(err, ErrRequestSystemIsNil) { + t.Fatalf("received: '%v', but expected: '%v'", err, ErrRequestSystemIsNil) + } + client := new(http.Client) + r = new(Requester) + err = r.SetHTTPClient(client) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v', but expected: '%v'", err, nil) + } + err = r.SetHTTPClient(client) + if !errors.Is(err, errCannotReuseHTTPClient) { + t.Fatalf("received: '%v', but expected: '%v'", err, errCannotReuseHTTPClient) + } +} + +func TestSetHTTPClientTimeout(t *testing.T) { + var r *Requester + err := r.SetHTTPClientTimeout(0) + if !errors.Is(err, ErrRequestSystemIsNil) { + t.Fatalf("received: '%v', but expected: '%v'", err, ErrRequestSystemIsNil) + } + r = new(Requester) + err = r.SetHTTPClient(common.NewHTTPClientWithTimeout(2)) + if err != nil { + t.Fatal(err) + } + err = r.SetHTTPClientTimeout(time.Second) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v', but expected: '%v'", err, nil) + } +} + +func TestSetHTTPClientUserAgent(t *testing.T) { + var r *Requester + err := r.SetHTTPClientUserAgent("") + if !errors.Is(err, ErrRequestSystemIsNil) { + t.Fatalf("received: '%v', but expected: '%v'", err, ErrRequestSystemIsNil) + } + r = new(Requester) + err = r.SetHTTPClientUserAgent("") + if !errors.Is(err, nil) { + t.Fatalf("received: '%v', but expected: '%v'", err, nil) + } +} + +func TestGetHTTPClientUserAgent(t *testing.T) { + var r *Requester + _, err := r.GetHTTPClientUserAgent() + if !errors.Is(err, ErrRequestSystemIsNil) { + t.Fatalf("received: '%v', but expected: '%v'", err, ErrRequestSystemIsNil) + } + r = new(Requester) + err = r.SetHTTPClientUserAgent("sillyness") + if !errors.Is(err, nil) { + t.Fatalf("received: '%v', but expected: '%v'", err, nil) + } + ua, err := r.GetHTTPClientUserAgent() + if !errors.Is(err, nil) { + t.Fatalf("received: '%v', but expected: '%v'", err, nil) + } + if ua != "sillyness" { + t.Fatal("unexpected value") + } +} diff --git a/exchanges/request/request_types.go b/exchanges/request/request_types.go index 6279d8fcbdf..7bc587017b2 100644 --- a/exchanges/request/request_types.go +++ b/exchanges/request/request_types.go @@ -28,11 +28,11 @@ var ( // Requester struct for the request client type Requester struct { - HTTPClient *http.Client + _HTTPClient *client limiter Limiter reporter Reporter - Name string - UserAgent string + name string + userAgent string maxRetries int jobs int32 Nonce nonce.Nonce diff --git a/exchanges/sharedtestvalues/customex.go b/exchanges/sharedtestvalues/customex.go index 155ebc8b52d..7ea2cb2278d 100644 --- a/exchanges/sharedtestvalues/customex.go +++ b/exchanges/sharedtestvalues/customex.go @@ -198,11 +198,12 @@ func (c *CustomEx) WithdrawFiatFundsToInternationalBank(ctx context.Context, wit return nil, nil } -func (c *CustomEx) SetHTTPClientUserAgent(ua string) { +func (c *CustomEx) SetHTTPClientUserAgent(ua string) error { + return nil } -func (c *CustomEx) GetHTTPClientUserAgent() string { - return "" +func (c *CustomEx) GetHTTPClientUserAgent() (string, error) { + return "", nil } func (c *CustomEx) SetClientProxyAddress(addr string) error { diff --git a/exchanges/stats/stats.go b/exchanges/stats/stats.go index 85b145dc502..9668a4818ba 100644 --- a/exchanges/stats/stats.go +++ b/exchanges/stats/stats.go @@ -61,7 +61,7 @@ func Add(exchange string, p currency.Pair, a asset.Item, price, volume float64) return errors.New("cannot add or update, invalid params") } - if p.Base == currency.XBT { + if p.Base.Equal(currency.XBT) { newPair, err := currency.NewPairFromStrings(currency.BTC.String(), p.Quote.String()) if err != nil { @@ -70,7 +70,7 @@ func Add(exchange string, p currency.Pair, a asset.Item, price, volume float64) Append(exchange, newPair, a, price, volume) } - if p.Quote == currency.USDT { + if p.Quote.Equal(currency.USDT) { newPair, err := currency.NewPairFromStrings(p.Base.String(), currency.USD.String()) if err != nil { return err diff --git a/exchanges/stream/websocket.go b/exchanges/stream/websocket.go index da171b5071b..7d27c86cbee 100644 --- a/exchanges/stream/websocket.go +++ b/exchanges/stream/websocket.go @@ -107,6 +107,9 @@ func (w *Websocket) Setup(s *WebsocketSetup) error { if w.features.Unsubscribe && s.Unsubscriber == nil { return fmt.Errorf("%s %w", w.exchangeName, errWebsocketUnsubscriberUnset) } + if s.ConnectionMonitorDelay <= 0 { + w.connectionMonitorDelay = defaultConnectionMonitorDelay + } w.Unsubscriber = s.Unsubscriber if s.GenerateSubscriptions == nil { @@ -339,10 +342,12 @@ func (w *Websocket) connectionMonitor() error { if w.checkAndSetMonitorRunning() { return errAlreadyRunning } + w.connectionMutex.RLock() + delay := w.connectionMonitorDelay + w.connectionMutex.RUnlock() go func() { - timer := time.NewTimer(connectionMonitorDelay) - + timer := time.NewTimer(delay) for { if w.verbose { log.Debugf(log.WebsocketMgr, @@ -395,7 +400,7 @@ func (w *Websocket) connectionMonitor() error { default: } } - timer.Reset(connectionMonitorDelay) + timer.Reset(delay) } } }() diff --git a/exchanges/stream/websocket_test.go b/exchanges/stream/websocket_test.go index 80ea41047a7..dd0d0e70a90 100644 --- a/exchanges/stream/websocket_test.go +++ b/exchanges/stream/websocket_test.go @@ -586,6 +586,7 @@ func TestResubscribe(t *testing.T) { func TestConnectionMonitorNoConnection(t *testing.T) { t.Parallel() ws := *New() + ws.connectionMonitorDelay = 500 ws.DataHandler = make(chan interface{}, 1) ws.ShutdownC = make(chan struct{}, 1) ws.exchangeName = "hello" @@ -602,27 +603,6 @@ func TestConnectionMonitorNoConnection(t *testing.T) { if !errors.Is(err, errAlreadyRunning) { t.Fatalf("received: %v, but expected: %v", err, errAlreadyRunning) } - if !ws.IsConnectionMonitorRunning() { - t.Fatal("Should not have exited") - } - ws.setEnabled(false) - time.Sleep(time.Second * 2) - if ws.IsConnectionMonitorRunning() { - t.Fatal("Should have exited") - } - ws.setConnectedStatus(true) // attempt shutdown when not enabled - ws.setConnectingStatus(true) // throw a spanner in the works - err = ws.connectionMonitor() - if !errors.Is(err, nil) { - t.Fatalf("received: %v, but expected: %v", err, nil) - } - if !ws.IsConnectionMonitorRunning() { - t.Fatal("Should not have exited") - } - time.Sleep(time.Millisecond * 100) - if ws.IsConnectionMonitorRunning() { - t.Fatal("Should have exited") - } } // TestSliceCopyDoesntImpactBoth logic test diff --git a/exchanges/stream/websocket_types.go b/exchanges/stream/websocket_types.go index 4c3c48277a1..3c39ec24513 100644 --- a/exchanges/stream/websocket_types.go +++ b/exchanges/stream/websocket_types.go @@ -16,8 +16,8 @@ import ( const ( // WebsocketNotEnabled alerts of a disabled websocket WebsocketNotEnabled = "exchange_websocket_not_enabled" - // connection monitor time delays and limits - connectionMonitorDelay = 2 * time.Second + // defaultConnectionMonitorDelay connection monitor time delays and limits + defaultConnectionMonitorDelay = 2 * time.Second WebsocketNotAuthenticatedUsingRest = "%v - Websocket not authenticated, using REST\n" Ping = "ping" Pong = "pong" @@ -37,6 +37,7 @@ type Websocket struct { trafficMonitorRunning bool dataMonitorRunning bool trafficTimeout time.Duration + connectionMonitorDelay time.Duration proxyAddr string defaultURL string defaultURLAuth string @@ -95,15 +96,16 @@ type Websocket struct { // WebsocketSetup defines variables for setting up a websocket connection type WebsocketSetup struct { - ExchangeConfig *config.Exchange - DefaultURL string - RunningURL string - RunningURLAuth string - Connector func() error - Subscriber func([]ChannelSubscription) error - Unsubscriber func([]ChannelSubscription) error - GenerateSubscriptions func() ([]ChannelSubscription, error) - Features *protocol.Features + ExchangeConfig *config.Exchange + DefaultURL string + RunningURL string + RunningURLAuth string + Connector func() error + Subscriber func([]ChannelSubscription) error + Unsubscriber func([]ChannelSubscription) error + GenerateSubscriptions func() ([]ChannelSubscription, error) + Features *protocol.Features + ConnectionMonitorDelay time.Duration // Local orderbook buffer config values SortBuffer bool SortBufferByUpdateIDs bool diff --git a/exchanges/ticker/ticker_test.go b/exchanges/ticker/ticker_test.go index 311abca198b..e5627806121 100644 --- a/exchanges/ticker/ticker_test.go +++ b/exchanges/ticker/ticker_test.go @@ -29,7 +29,7 @@ func TestMain(m *testing.M) { var cpyMux *dispatch.Mux func TestSubscribeTicker(t *testing.T) { - _, err := SubscribeTicker("", currency.Pair{}, asset.Item("")) + _, err := SubscribeTicker("", currency.EMPTYPAIR, asset.Item("")) if err == nil { t.Error("error cannot be nil") } diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go index 56f73a1bb46..e36e82bb84e 100644 --- a/exchanges/yobit/yobit.go +++ b/exchanges/yobit/yobit.go @@ -372,7 +372,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, amount float64, bankTran switch bankTransactionType { case exchange.PerfectMoney: - if c.Match(currency.USD) { + if c.Equal(currency.USD) { fee = 0.02 * amount } case exchange.Payeer: @@ -394,7 +394,7 @@ func getInternationalBankWithdrawalFee(c currency.Code, amount float64, bankTran fee = 0.04 * amount } case exchange.Capitalist: - if c.Match(currency.USD) { + if c.Equal(currency.USD) { fee = 0.06 * amount } } @@ -407,7 +407,7 @@ func getInternationalBankDepositFee(c currency.Code, bankTransactionType exchang var fee float64 switch bankTransactionType { case exchange.PerfectMoney: - if c.Match(currency.USD) { + if c.Equal(currency.USD) { fee = 0 } case exchange.Payeer: diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index 0f7d37879ce..6a0e51de3ba 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -125,7 +125,7 @@ func TestGetOpenOrders(t *testing.T) { func TestGetOrderInfo(t *testing.T) { t.Parallel() _, err := y.GetOrderInfo(context.Background(), - "6196974", currency.Pair{}, asset.Spot) + "6196974", currency.EMPTYPAIR, asset.Spot) if err == nil { t.Error("GetOrderInfo() Expected error") } diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 3dbeea5a0bb..832c7cb047e 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -97,10 +97,13 @@ func (y *Yobit) SetDefaults() { }, } - y.Requester = request.New(y.Name, + y.Requester, err = request.New(y.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), // Server responses are cached every 2 seconds. request.WithLimiter(request.NewBasicRateLimit(time.Second, 1))) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } y.API.Endpoints = y.NewEndpoints() err = y.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: apiPublicURL, @@ -308,11 +311,12 @@ func (y *Yobit) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac for x, y := range accountBalance.FundsInclOrders { var exchangeCurrency account.Balance exchangeCurrency.CurrencyName = currency.NewCode(x) - exchangeCurrency.TotalValue = y + exchangeCurrency.Total = y exchangeCurrency.Hold = 0 for z, w := range accountBalance.Funds { if z == x { exchangeCurrency.Hold = y - w + exchangeCurrency.Free = w } } diff --git a/exchanges/zb/zb_mock_test.go b/exchanges/zb/zb_mock_test.go index f1ac27e77b8..158efa02a03 100644 --- a/exchanges/zb/zb_mock_test.go +++ b/exchanges/zb/zb_mock_test.go @@ -47,7 +47,10 @@ func TestMain(m *testing.M) { log.Fatalf("Mock server error %s", err) } - z.HTTPClient = newClient + err = z.SetHTTPClient(newClient) + if err != nil { + log.Fatalf("Mock server error %s", err) + } endpoints := z.API.Endpoints.GetURLMap() for k := range endpoints { err = z.API.Endpoints.SetRunning(k, serverDetails) diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index 8d322585ce8..c691e068613 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -971,15 +971,15 @@ func Test_FormatExchangeKlineInterval(t *testing.T) { } func TestValidateCandlesRequest(t *testing.T) { - _, err := z.validateCandlesRequest(currency.Pair{}, "", time.Time{}, time.Time{}, kline.Interval(-1)) + _, err := z.validateCandlesRequest(currency.EMPTYPAIR, "", time.Time{}, time.Time{}, kline.Interval(-1)) if !errors.Is(err, common.ErrDateUnset) { t.Error(err) } - _, err = z.validateCandlesRequest(currency.Pair{}, "", time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1)) + _, err = z.validateCandlesRequest(currency.EMPTYPAIR, "", time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1)) if !errors.Is(err, common.ErrDateUnset) { t.Error(err) } - _, err = z.validateCandlesRequest(currency.Pair{}, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour) + _, err = z.validateCandlesRequest(currency.EMPTYPAIR, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour) if err != nil && err.Error() != "pair not enabled" { t.Error(err) } diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index e010399314f..e278a2d102c 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -130,9 +130,12 @@ func (z *ZB) SetDefaults() { }, } - z.Requester = request.New(z.Name, + z.Requester, err = request.New(z.Name, common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } z.API.Endpoints = z.NewEndpoints() err = z.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ exchange.RestSpot: zbTradeURL, @@ -380,8 +383,9 @@ func (z *ZB) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (accou balances = append(balances, account.Balance{ CurrencyName: currency.NewCode(coins[i].EnName), - TotalValue: hold + avail, + Total: hold + avail, Hold: hold, + Free: avail, }) } diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 826cfd39658..34f0bd3787d 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -1914,9 +1914,12 @@ type AccountCurrencyInfo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` - TotalValue float64 `protobuf:"fixed64,2,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"` - Hold float64 `protobuf:"fixed64,3,opt,name=hold,proto3" json:"hold,omitempty"` + Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` + TotalValue float64 `protobuf:"fixed64,2,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"` + Hold float64 `protobuf:"fixed64,3,opt,name=hold,proto3" json:"hold,omitempty"` + Free float64 `protobuf:"fixed64,4,opt,name=free,proto3" json:"free,omitempty"` + FreeWithoutBorrow float64 `protobuf:"fixed64,5,opt,name=freeWithoutBorrow,proto3" json:"freeWithoutBorrow,omitempty"` + Borrowed float64 `protobuf:"fixed64,6,opt,name=borrowed,proto3" json:"borrowed,omitempty"` } func (x *AccountCurrencyInfo) Reset() { @@ -1972,6 +1975,27 @@ func (x *AccountCurrencyInfo) GetHold() float64 { return 0 } +func (x *AccountCurrencyInfo) GetFree() float64 { + if x != nil { + return x.Free + } + return 0 +} + +func (x *AccountCurrencyInfo) GetFreeWithoutBorrow() float64 { + if x != nil { + return x.FreeWithoutBorrow + } + return 0 +} + +func (x *AccountCurrencyInfo) GetBorrowed() float64 { + if x != nil { + return x.Borrowed + } + return 0 +} + type GetAccountInfoResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -10776,7 +10800,7 @@ type GetFuturesPositionsRequest struct { StartDate string `protobuf:"bytes,4,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` EndDate string `protobuf:"bytes,5,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` - PositionLimit int64 `protobuf:"varint,7,opt,name=positionLimit,proto3" json:"positionLimit,omitempty"` + PositionLimit int64 `protobuf:"varint,7,opt,name=position_limit,json=positionLimit,proto3" json:"position_limit,omitempty"` Verbose bool `protobuf:"varint,8,opt,name=verbose,proto3" json:"verbose,omitempty"` Overwrite bool `protobuf:"varint,9,opt,name=overwrite,proto3" json:"overwrite,omitempty"` } @@ -10881,10 +10905,10 @@ type GetFuturesPositionsResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TotalOrders int64 `protobuf:"varint,1,opt,name=totalOrders,proto3" json:"totalOrders,omitempty"` - SubAccount string `protobuf:"bytes,2,opt,name=subAccount,proto3" json:"subAccount,omitempty"` - TotalRealisedPNL string `protobuf:"bytes,3,opt,name=totalRealisedPNL,proto3" json:"totalRealisedPNL,omitempty"` - TotalUnrealisedPNL string `protobuf:"bytes,4,opt,name=totalUnrealisedPNL,proto3" json:"totalUnrealisedPNL,omitempty"` + TotalOrders int64 `protobuf:"varint,1,opt,name=total_orders,json=totalOrders,proto3" json:"total_orders,omitempty"` + SubAccount string `protobuf:"bytes,2,opt,name=sub_account,json=subAccount,proto3" json:"sub_account,omitempty"` + TotalRealisedPNL string `protobuf:"bytes,3,opt,name=total_realisedPNL,json=totalRealisedPNL,proto3" json:"total_realisedPNL,omitempty"` + TotalUnrealisedPNL string `protobuf:"bytes,4,opt,name=total_unrealisedPNL,json=totalUnrealisedPNL,proto3" json:"total_unrealisedPNL,omitempty"` TotalPNL string `protobuf:"bytes,5,opt,name=totalPNL,proto3" json:"totalPNL,omitempty"` Positions []*FuturePosition `protobuf:"bytes,6,rep,name=positions,proto3" json:"positions,omitempty"` } @@ -10969,11 +10993,11 @@ type FuturePosition struct { unknownFields protoimpl.UnknownFields Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - CurrentDirection string `protobuf:"bytes,2,opt,name=currentDirection,proto3" json:"currentDirection,omitempty"` + CurrentDirection string `protobuf:"bytes,2,opt,name=current_direction,json=currentDirection,proto3" json:"current_direction,omitempty"` UnrealisedPNL string `protobuf:"bytes,3,opt,name=unrealisedPNL,proto3" json:"unrealisedPNL,omitempty"` RealisedPNL string `protobuf:"bytes,4,opt,name=realisedPNL,proto3" json:"realisedPNL,omitempty"` - OpeningDate string `protobuf:"bytes,5,opt,name=openingDate,proto3" json:"openingDate,omitempty"` - ClosingDate string `protobuf:"bytes,6,opt,name=closingDate,proto3" json:"closingDate,omitempty"` + OpeningDate string `protobuf:"bytes,5,opt,name=opening_date,json=openingDate,proto3" json:"opening_date,omitempty"` + ClosingDate string `protobuf:"bytes,6,opt,name=closing_date,json=closingDate,proto3" json:"closing_date,omitempty"` Orders []*OrderDetails `protobuf:"bytes,7,rep,name=orders,proto3" json:"orders,omitempty"` } @@ -11065,10 +11089,10 @@ type GetCollateralRequest struct { Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` - SubAccount string `protobuf:"bytes,3,opt,name=subAccount,proto3" json:"subAccount,omitempty"` - IncludeBreakdown bool `protobuf:"varint,4,opt,name=includeBreakdown,proto3" json:"includeBreakdown,omitempty"` - CalculateOffline bool `protobuf:"varint,5,opt,name=calculateOffline,proto3" json:"calculateOffline,omitempty"` - IncludeZeroValues bool `protobuf:"varint,6,opt,name=includeZeroValues,proto3" json:"includeZeroValues,omitempty"` + SubAccount string `protobuf:"bytes,3,opt,name=sub_account,json=subAccount,proto3" json:"sub_account,omitempty"` + IncludeBreakdown bool `protobuf:"varint,4,opt,name=include_breakdown,json=includeBreakdown,proto3" json:"include_breakdown,omitempty"` + CalculateOffline bool `protobuf:"varint,5,opt,name=calculate_offline,json=calculateOffline,proto3" json:"calculate_offline,omitempty"` + IncludeZeroValues bool `protobuf:"varint,6,opt,name=include_zero_values,json=includeZeroValues,proto3" json:"include_zero_values,omitempty"` } func (x *GetCollateralRequest) Reset() { @@ -11150,9 +11174,17 @@ type GetCollateralResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SubAccount string `protobuf:"bytes,1,opt,name=subAccount,proto3" json:"subAccount,omitempty"` - TotalCollateral string `protobuf:"bytes,2,opt,name=totalCollateral,proto3" json:"totalCollateral,omitempty"` - CurrencyBreakdown []*CollateralForCurrency `protobuf:"bytes,3,rep,name=currencyBreakdown,proto3" json:"currencyBreakdown,omitempty"` + SubAccount string `protobuf:"bytes,1,opt,name=sub_account,json=subAccount,proto3" json:"sub_account,omitempty"` + CollateralCurrency string `protobuf:"bytes,2,opt,name=collateral_currency,json=collateralCurrency,proto3" json:"collateral_currency,omitempty"` + TotalValueOfPositiveSpotBalances string `protobuf:"bytes,3,opt,name=total_value_of_positive_spot_balances,json=totalValueOfPositiveSpotBalances,proto3" json:"total_value_of_positive_spot_balances,omitempty"` + CollateralContributedByPositiveSpotBalances string `protobuf:"bytes,4,opt,name=collateral_contributed_by_positive_spot_balances,json=collateralContributedByPositiveSpotBalances,proto3" json:"collateral_contributed_by_positive_spot_balances,omitempty"` + UsedCollateral string `protobuf:"bytes,5,opt,name=used_collateral,json=usedCollateral,proto3" json:"used_collateral,omitempty"` + UsedBreakdown *CollateralUsedBreakdown `protobuf:"bytes,6,opt,name=used_breakdown,json=usedBreakdown,proto3" json:"used_breakdown,omitempty"` + AvailableCollateral string `protobuf:"bytes,7,opt,name=available_collateral,json=availableCollateral,proto3" json:"available_collateral,omitempty"` + MaintenanceCollateral string `protobuf:"bytes,8,opt,name=maintenance_collateral,json=maintenanceCollateral,proto3" json:"maintenance_collateral,omitempty"` + UnrealisedPNL string `protobuf:"bytes,9,opt,name=unrealisedPNL,proto3" json:"unrealisedPNL,omitempty"` + CurrencyBreakdown []*CollateralForCurrency `protobuf:"bytes,10,rep,name=currency_breakdown,json=currencyBreakdown,proto3" json:"currency_breakdown,omitempty"` + PositionBreakdown []*CollateralByPosition `protobuf:"bytes,11,rep,name=position_breakdown,json=positionBreakdown,proto3" json:"position_breakdown,omitempty"` } func (x *GetCollateralResponse) Reset() { @@ -11194,9 +11226,58 @@ func (x *GetCollateralResponse) GetSubAccount() string { return "" } -func (x *GetCollateralResponse) GetTotalCollateral() string { +func (x *GetCollateralResponse) GetCollateralCurrency() string { + if x != nil { + return x.CollateralCurrency + } + return "" +} + +func (x *GetCollateralResponse) GetTotalValueOfPositiveSpotBalances() string { + if x != nil { + return x.TotalValueOfPositiveSpotBalances + } + return "" +} + +func (x *GetCollateralResponse) GetCollateralContributedByPositiveSpotBalances() string { + if x != nil { + return x.CollateralContributedByPositiveSpotBalances + } + return "" +} + +func (x *GetCollateralResponse) GetUsedCollateral() string { + if x != nil { + return x.UsedCollateral + } + return "" +} + +func (x *GetCollateralResponse) GetUsedBreakdown() *CollateralUsedBreakdown { + if x != nil { + return x.UsedBreakdown + } + return nil +} + +func (x *GetCollateralResponse) GetAvailableCollateral() string { + if x != nil { + return x.AvailableCollateral + } + return "" +} + +func (x *GetCollateralResponse) GetMaintenanceCollateral() string { + if x != nil { + return x.MaintenanceCollateral + } + return "" +} + +func (x *GetCollateralResponse) GetUnrealisedPNL() string { if x != nil { - return x.TotalCollateral + return x.UnrealisedPNL } return "" } @@ -11208,16 +11289,31 @@ func (x *GetCollateralResponse) GetCurrencyBreakdown() []*CollateralForCurrency return nil } +func (x *GetCollateralResponse) GetPositionBreakdown() []*CollateralByPosition { + if x != nil { + return x.PositionBreakdown + } + return nil +} + type CollateralForCurrency struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` - OriginalAmount string `protobuf:"bytes,2,opt,name=originalAmount,proto3" json:"originalAmount,omitempty"` - ScaledCollateral string `protobuf:"bytes,3,opt,name=scaledCollateral,proto3" json:"scaledCollateral,omitempty"` - ScaledToCurrency string `protobuf:"bytes,4,opt,name=scaledToCurrency,proto3" json:"scaledToCurrency,omitempty"` - Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"` + Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` + ExcludedFromCollateral bool `protobuf:"varint,2,opt,name=excluded_from_collateral,json=excludedFromCollateral,proto3" json:"excluded_from_collateral,omitempty"` + TotalFunds string `protobuf:"bytes,3,opt,name=total_funds,json=totalFunds,proto3" json:"total_funds,omitempty"` + AvailableForUseAsCollateral string `protobuf:"bytes,4,opt,name=available_for_use_as_collateral,json=availableForUseAsCollateral,proto3" json:"available_for_use_as_collateral,omitempty"` + ApproxFairMarketValue string `protobuf:"bytes,5,opt,name=approx_fair_market_value,json=approxFairMarketValue,proto3" json:"approx_fair_market_value,omitempty"` + Weighting string `protobuf:"bytes,6,opt,name=weighting,proto3" json:"weighting,omitempty"` + CollateralContribution string `protobuf:"bytes,7,opt,name=collateral_contribution,json=collateralContribution,proto3" json:"collateral_contribution,omitempty"` + ScaledToCurrency string `protobuf:"bytes,8,opt,name=scaled_to_currency,json=scaledToCurrency,proto3" json:"scaled_to_currency,omitempty"` + Unrealised_PNL string `protobuf:"bytes,9,opt,name=unrealised_PNL,json=unrealisedPNL,proto3" json:"unrealised_PNL,omitempty"` + FundsInUse string `protobuf:"bytes,10,opt,name=funds_in_use,json=fundsInUse,proto3" json:"funds_in_use,omitempty"` + AdditionalCollateralUsed string `protobuf:"bytes,11,opt,name=additional_collateral_used,json=additionalCollateralUsed,proto3" json:"additional_collateral_used,omitempty"` + UsedBreakdown *CollateralUsedBreakdown `protobuf:"bytes,12,opt,name=used_breakdown,json=usedBreakdown,proto3" json:"used_breakdown,omitempty"` + Error string `protobuf:"bytes,13,opt,name=error,proto3" json:"error,omitempty"` } func (x *CollateralForCurrency) Reset() { @@ -11259,16 +11355,44 @@ func (x *CollateralForCurrency) GetCurrency() string { return "" } -func (x *CollateralForCurrency) GetOriginalAmount() string { +func (x *CollateralForCurrency) GetExcludedFromCollateral() bool { + if x != nil { + return x.ExcludedFromCollateral + } + return false +} + +func (x *CollateralForCurrency) GetTotalFunds() string { + if x != nil { + return x.TotalFunds + } + return "" +} + +func (x *CollateralForCurrency) GetAvailableForUseAsCollateral() string { + if x != nil { + return x.AvailableForUseAsCollateral + } + return "" +} + +func (x *CollateralForCurrency) GetApproxFairMarketValue() string { + if x != nil { + return x.ApproxFairMarketValue + } + return "" +} + +func (x *CollateralForCurrency) GetWeighting() string { if x != nil { - return x.OriginalAmount + return x.Weighting } return "" } -func (x *CollateralForCurrency) GetScaledCollateral() string { +func (x *CollateralForCurrency) GetCollateralContribution() string { if x != nil { - return x.ScaledCollateral + return x.CollateralContribution } return "" } @@ -11280,6 +11404,34 @@ func (x *CollateralForCurrency) GetScaledToCurrency() string { return "" } +func (x *CollateralForCurrency) GetUnrealised_PNL() string { + if x != nil { + return x.Unrealised_PNL + } + return "" +} + +func (x *CollateralForCurrency) GetFundsInUse() string { + if x != nil { + return x.FundsInUse + } + return "" +} + +func (x *CollateralForCurrency) GetAdditionalCollateralUsed() string { + if x != nil { + return x.AdditionalCollateralUsed + } + return "" +} + +func (x *CollateralForCurrency) GetUsedBreakdown() *CollateralUsedBreakdown { + if x != nil { + return x.UsedBreakdown + } + return nil +} + func (x *CollateralForCurrency) GetError() string { if x != nil { return x.Error @@ -11287,6 +11439,204 @@ func (x *CollateralForCurrency) GetError() string { return "" } +type CollateralByPosition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` + Size string `protobuf:"bytes,2,opt,name=size,proto3" json:"size,omitempty"` + OpenOrderSize string `protobuf:"bytes,3,opt,name=open_order_size,json=openOrderSize,proto3" json:"open_order_size,omitempty"` + PositionSize string `protobuf:"bytes,4,opt,name=position_size,json=positionSize,proto3" json:"position_size,omitempty"` + MarkPrice string `protobuf:"bytes,5,opt,name=mark_price,json=markPrice,proto3" json:"mark_price,omitempty"` + RequiredMargin string `protobuf:"bytes,6,opt,name=required_margin,json=requiredMargin,proto3" json:"required_margin,omitempty"` + TotalCollateralUsed string `protobuf:"bytes,7,opt,name=total_collateral_used,json=totalCollateralUsed,proto3" json:"total_collateral_used,omitempty"` +} + +func (x *CollateralByPosition) Reset() { + *x = CollateralByPosition{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[175] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CollateralByPosition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CollateralByPosition) ProtoMessage() {} + +func (x *CollateralByPosition) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[175] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CollateralByPosition.ProtoReflect.Descriptor instead. +func (*CollateralByPosition) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{175} +} + +func (x *CollateralByPosition) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *CollateralByPosition) GetSize() string { + if x != nil { + return x.Size + } + return "" +} + +func (x *CollateralByPosition) GetOpenOrderSize() string { + if x != nil { + return x.OpenOrderSize + } + return "" +} + +func (x *CollateralByPosition) GetPositionSize() string { + if x != nil { + return x.PositionSize + } + return "" +} + +func (x *CollateralByPosition) GetMarkPrice() string { + if x != nil { + return x.MarkPrice + } + return "" +} + +func (x *CollateralByPosition) GetRequiredMargin() string { + if x != nil { + return x.RequiredMargin + } + return "" +} + +func (x *CollateralByPosition) GetTotalCollateralUsed() string { + if x != nil { + return x.TotalCollateralUsed + } + return "" +} + +type CollateralUsedBreakdown struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LockedInStakes string `protobuf:"bytes,1,opt,name=locked_in_stakes,json=lockedInStakes,proto3" json:"locked_in_stakes,omitempty"` + LockedIn_NFTBids string `protobuf:"bytes,2,opt,name=locked_in_NFT_bids,json=lockedInNFTBids,proto3" json:"locked_in_NFT_bids,omitempty"` + LockedInFeeVoucher string `protobuf:"bytes,3,opt,name=locked_in_fee_voucher,json=lockedInFeeVoucher,proto3" json:"locked_in_fee_voucher,omitempty"` + LockedInSpotMarginFundingOffers string `protobuf:"bytes,4,opt,name=locked_in_spot_margin_funding_offers,json=lockedInSpotMarginFundingOffers,proto3" json:"locked_in_spot_margin_funding_offers,omitempty"` + LockedInSpotOrders string `protobuf:"bytes,5,opt,name=locked_in_spot_orders,json=lockedInSpotOrders,proto3" json:"locked_in_spot_orders,omitempty"` + LockedAsCollateral string `protobuf:"bytes,6,opt,name=locked_as_collateral,json=lockedAsCollateral,proto3" json:"locked_as_collateral,omitempty"` + UsedInFutures string `protobuf:"bytes,7,opt,name=used_in_futures,json=usedInFutures,proto3" json:"used_in_futures,omitempty"` + UsedInSpotMargin string `protobuf:"bytes,8,opt,name=used_in_spot_margin,json=usedInSpotMargin,proto3" json:"used_in_spot_margin,omitempty"` +} + +func (x *CollateralUsedBreakdown) Reset() { + *x = CollateralUsedBreakdown{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[176] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CollateralUsedBreakdown) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CollateralUsedBreakdown) ProtoMessage() {} + +func (x *CollateralUsedBreakdown) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[176] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CollateralUsedBreakdown.ProtoReflect.Descriptor instead. +func (*CollateralUsedBreakdown) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{176} +} + +func (x *CollateralUsedBreakdown) GetLockedInStakes() string { + if x != nil { + return x.LockedInStakes + } + return "" +} + +func (x *CollateralUsedBreakdown) GetLockedIn_NFTBids() string { + if x != nil { + return x.LockedIn_NFTBids + } + return "" +} + +func (x *CollateralUsedBreakdown) GetLockedInFeeVoucher() string { + if x != nil { + return x.LockedInFeeVoucher + } + return "" +} + +func (x *CollateralUsedBreakdown) GetLockedInSpotMarginFundingOffers() string { + if x != nil { + return x.LockedInSpotMarginFundingOffers + } + return "" +} + +func (x *CollateralUsedBreakdown) GetLockedInSpotOrders() string { + if x != nil { + return x.LockedInSpotOrders + } + return "" +} + +func (x *CollateralUsedBreakdown) GetLockedAsCollateral() string { + if x != nil { + return x.LockedAsCollateral + } + return "" +} + +func (x *CollateralUsedBreakdown) GetUsedInFutures() string { + if x != nil { + return x.UsedInFutures + } + return "" +} + +func (x *CollateralUsedBreakdown) GetUsedInSpotMargin() string { + if x != nil { + return x.UsedInSpotMargin + } + return "" +} + type CancelBatchOrdersResponse_Orders struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -11298,7 +11648,7 @@ type CancelBatchOrdersResponse_Orders struct { func (x *CancelBatchOrdersResponse_Orders) Reset() { *x = CancelBatchOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[185] + mi := &file_rpc_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11311,7 +11661,7 @@ func (x *CancelBatchOrdersResponse_Orders) String() string { func (*CancelBatchOrdersResponse_Orders) ProtoMessage() {} func (x *CancelBatchOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[185] + mi := &file_rpc_proto_msgTypes[187] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11346,7 +11696,7 @@ type CancelAllOrdersResponse_Orders struct { func (x *CancelAllOrdersResponse_Orders) Reset() { *x = CancelAllOrdersResponse_Orders{} if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[187] + mi := &file_rpc_proto_msgTypes[189] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11359,7 +11709,7 @@ func (x *CancelAllOrdersResponse_Orders) String() string { func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} func (x *CancelAllOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[187] + mi := &file_rpc_proto_msgTypes[189] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11653,1989 +12003,2100 @@ var file_rpc_proto_rawDesc = []byte{ 0x72, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x66, 0x0a, 0x13, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, - 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x61, - 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x27, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x85, - 0x01, 0x0a, 0x10, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, - 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4e, 0x0a, - 0x14, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x22, 0x1c, 0x0a, - 0x1a, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6e, 0x0a, 0x04, 0x43, - 0x6f, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x70, - 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x68, 0x0a, 0x12, 0x4f, - 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, - 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x4d, 0x0a, 0x11, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, - 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0xc4, 0x01, 0x0a, 0x13, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, + 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x68, 0x6f, 0x6c, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x72, 0x65, 0x65, 0x57, 0x69, 0x74, 0x68, 0x6f, + 0x75, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x11, + 0x66, 0x72, 0x65, 0x65, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, + 0x77, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x08, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x65, 0x64, 0x22, 0x61, 0x0a, + 0x16, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x27, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x85, 0x01, + 0x0a, 0x10, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, + 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4e, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x22, 0x1c, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6e, 0x0a, 0x04, 0x43, 0x6f, + 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, + 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x68, 0x0a, 0x12, 0x4f, 0x66, + 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, - 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, - 0x74, 0x61, 0x67, 0x65, 0x22, 0x48, 0x0a, 0x0c, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, - 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x98, - 0x01, 0x0a, 0x0b, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x34, - 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, - 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x63, - 0x6f, 0x69, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x0a, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcb, 0x04, 0x0a, 0x1b, 0x47, 0x65, - 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0b, 0x63, 0x6f, 0x69, - 0x6e, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0a, 0x63, 0x6f, - 0x69, 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x0d, 0x63, 0x6f, 0x69, 0x6e, - 0x73, 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0c, 0x63, - 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x70, 0x0a, 0x15, 0x63, - 0x6f, 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x61, 0x67, 0x65, 0x22, 0x4d, 0x0a, 0x11, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, + 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, + 0x61, 0x67, 0x65, 0x22, 0x48, 0x0a, 0x0c, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, + 0x69, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x98, 0x01, + 0x0a, 0x0b, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x34, 0x0a, + 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, + 0x73, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x63, 0x6f, + 0x69, 0x6e, 0x73, 0x1a, 0x53, 0x0a, 0x0a, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcb, 0x04, 0x0a, 0x1b, 0x47, 0x65, 0x74, + 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0b, 0x63, 0x6f, 0x69, 0x6e, + 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x69, + 0x6e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x0d, 0x63, 0x6f, 0x69, 0x6e, 0x73, + 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0c, 0x63, 0x6f, + 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x70, 0x0a, 0x15, 0x63, 0x6f, + 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, + 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x66, + 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2f, 0x0a, 0x0c, + 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, + 0x52, 0x0b, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x6d, 0x0a, + 0x14, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x1a, 0x5c, 0x0a, 0x18, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, - 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2f, 0x0a, - 0x0c, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, - 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x6d, - 0x0a, 0x14, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x69, 0x6e, 0x73, - 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x1a, 0x5c, 0x0a, - 0x18, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x17, 0x43, - 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, - 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x78, 0x0a, - 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x69, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x46, 0x6f, - 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0xed, 0x01, 0x0a, 0x0d, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x2c, 0x0a, - 0x12, 0x72, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, - 0x6c, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x73, 0x74, 0x50, - 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x61, - 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x70, - 0x69, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x70, 0x69, - 0x4b, 0x65, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x22, 0x5b, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3e, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x52, 0x0e, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, - 0x22, 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x14, 0x46, 0x6f, 0x72, 0x65, - 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, - 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x56, 0x0a, 0x15, 0x47, - 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, 0x72, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, - 0x74, 0x65, 0x73, 0x22, 0x8c, 0x04, 0x0a, 0x0c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x62, 0x61, 0x73, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x25, 0x0a, - 0x0e, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x64, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, - 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, - 0x63, 0x6f, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x11, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, - 0x65, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, - 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x22, 0x41, 0x0a, 0x11, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, - 0x88, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x17, 0x43, 0x6f, + 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x73, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, + 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x63, 0x6f, 0x6c, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x78, 0x0a, 0x1d, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, + 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0xed, 0x01, 0x0a, 0x0d, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, + 0x72, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x73, 0x74, 0x50, 0x6f, + 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x70, + 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x70, 0x69, + 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x70, 0x69, 0x4b, + 0x65, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6d, 0x61, + 0x72, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x22, 0x5b, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3e, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, + 0x0e, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, + 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x14, 0x46, 0x6f, 0x72, 0x65, 0x78, + 0x52, 0x61, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x69, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x56, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, 0x72, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, + 0x65, 0x73, 0x22, 0x8c, 0x04, 0x0a, 0x0c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, - 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, - 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xf7, 0x01, 0x0a, 0x12, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x22, 0x65, 0x0a, 0x06, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x1b, - 0x0a, 0x09, 0x66, 0x65, 0x65, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x66, 0x65, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x22, 0x7b, 0x0a, 0x13, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x50, - 0x6c, 0x61, 0x63, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x26, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x14, 0x53, 0x69, 0x6d, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, - 0x69, 0x64, 0x65, 0x22, 0xf2, 0x01, 0x0a, 0x15, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, - 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6d, 0x69, 0x6e, - 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, - 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x30, - 0x0a, 0x14, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x67, 0x61, 0x69, - 0x6e, 0x5f, 0x6c, 0x6f, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x70, 0x65, - 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x47, 0x61, 0x69, 0x6e, 0x4c, 0x6f, 0x73, 0x73, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x10, 0x57, 0x68, 0x61, - 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x12, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x26, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x62, 0x61, 0x73, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x25, 0x0a, 0x0e, + 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x64, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, 0x64, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x66, 0x65, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x63, + 0x6f, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x11, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, + 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x22, 0xf6, 0x01, 0x0a, 0x18, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x49, 0x64, - 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x73, 0x69, 0x64, 0x65, 0x22, 0x86, 0x02, 0x0a, 0x19, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x1a, 0xa6, 0x01, 0x0a, 0x06, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x5c, 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x22, 0x41, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0x88, + 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, + 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xf7, 0x01, 0x0a, 0x12, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x22, 0x65, 0x0a, 0x06, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, + 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x66, 0x65, 0x65, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x66, 0x65, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x22, 0x7b, 0x0a, 0x13, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x26, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, + 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x22, 0x88, 0x01, 0x0a, 0x14, 0x53, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, + 0x64, 0x65, 0x22, 0xf2, 0x01, 0x0a, 0x15, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x49, + 0x74, 0x65, 0x6d, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, + 0x6d, 0x75, 0x6d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x69, + 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0c, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a, + 0x14, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x67, 0x61, 0x69, 0x6e, + 0x5f, 0x6c, 0x6f, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x70, 0x65, 0x72, + 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x47, 0x61, 0x69, 0x6e, 0x4c, 0x6f, 0x73, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x10, 0x57, 0x68, 0x61, 0x6c, + 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x12, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x22, 0xf6, 0x01, 0x0a, 0x18, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x2e, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3e, 0x0a, - 0x10, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x34, 0x0a, - 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x22, 0xb2, 0x02, 0x0a, 0x17, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3e, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, - 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0xc0, 0x01, 0x0a, 0x06, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x5a, 0x0a, 0x0c, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xae, 0x01, 0x0a, - 0x0f, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, - 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x62, 0x69, - 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, - 0x69, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, 0x73, 0x6b, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x73, - 0x6b, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x5f, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf5, 0x01, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, - 0x74, 0x65, 0x6d, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x64, 0x22, 0xe6, 0x01, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0f, 0x63, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, - 0x0a, 0x10, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, - 0x69, 0x64, 0x22, 0x24, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x28, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x22, 0x52, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x14, - 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x22, 0x48, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0xe3, - 0x01, 0x0a, 0x29, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x09, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x40, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x0e, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9a, 0x01, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x49, 0x64, 0x12, + 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, + 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, + 0x69, 0x64, 0x65, 0x22, 0x86, 0x02, 0x0a, 0x19, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x40, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x1a, 0xa6, 0x01, 0x0a, 0x06, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x5c, + 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3e, 0x0a, 0x10, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x34, 0x0a, 0x16, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x22, 0xb2, 0x02, 0x0a, 0x17, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, + 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0xc0, 0x01, 0x0a, 0x06, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x79, 0x70, - 0x61, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, 0x79, 0x70, 0x61, 0x73, - 0x73, 0x22, 0x55, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x67, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x41, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x22, 0x3c, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, - 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x22, - 0xaf, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, - 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x61, 0x6e, - 0x6b, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x6e, 0x6b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x64, 0x22, 0xec, 0x01, 0x0a, 0x15, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x61, 0x67, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, - 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x22, 0x3a, 0x0a, 0x10, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x1a, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, - 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, - 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x22, 0x81, 0x01, 0x0a, 0x21, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, + 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x5a, 0x0a, 0x0c, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x37, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xae, 0x01, 0x0a, 0x0f, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, + 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x62, 0x69, 0x64, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x69, + 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x61, 0x73, 0x6b, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x73, 0x6b, + 0x73, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x5f, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf5, 0x01, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x64, 0x22, 0xe6, 0x01, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, 0x0a, + 0x10, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, + 0x64, 0x22, 0x24, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x28, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, + 0x52, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, + 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x22, 0x48, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0xe3, 0x01, + 0x0a, 0x29, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x09, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x0e, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x9a, 0x01, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x79, 0x70, 0x61, + 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, 0x79, 0x70, 0x61, 0x73, 0x73, + 0x22, 0x55, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x67, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x22, 0x3c, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x22, 0xaf, + 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x22, 0x79, 0x0a, 0x1d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x16, + 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x61, 0x6e, 0x6b, + 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x62, 0x61, 0x6e, 0x6b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, + 0x22, 0xec, 0x01, 0x0a, 0x15, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x61, 0x67, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x61, + 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, + 0x3a, 0x0a, 0x10, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x1a, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, + 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, + 0x81, 0x01, 0x0a, 0x21, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, - 0x5b, 0x0a, 0x22, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x95, 0x02, 0x0a, - 0x17, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6c, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, - 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x64, 0x41, 0x74, 0x22, 0x54, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xea, 0x01, 0x0a, 0x16, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x2f, 0x0a, 0x04, 0x66, 0x69, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x61, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x69, 0x61, 0x74, - 0x12, 0x35, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x22, 0xb8, 0x01, 0x0a, 0x13, 0x46, 0x69, 0x61, 0x74, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, - 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x73, 0x62, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x62, 0x73, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x69, 0x66, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x77, 0x69, 0x66, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x69, 0x62, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x62, - 0x61, 0x6e, 0x22, 0x79, 0x0a, 0x15, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x5f, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x54, 0x61, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x22, 0x31, 0x0a, - 0x17, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x22, 0x6e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, - 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0x47, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, - 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x4b, 0x0a, 0x17, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x22, 0x79, 0x0a, 0x1d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xd8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x5a, 0x0a, 0x14, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x97, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x70, 0x61, - 0x69, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x80, 0x01, 0x0a, 0x19, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, - 0x0a, 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x5b, + 0x0a, 0x22, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x95, 0x02, 0x0a, 0x17, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6c, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x22, 0x54, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6c, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xea, 0x01, 0x0a, 0x16, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2f, + 0x0a, 0x04, 0x66, 0x69, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x61, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x69, 0x61, 0x74, 0x12, + 0x35, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x22, 0xb8, 0x01, 0x0a, 0x13, 0x46, 0x69, 0x61, 0x74, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x73, 0x62, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x62, 0x73, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x69, 0x66, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x77, 0x69, 0x66, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x69, 0x62, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x62, 0x61, + 0x6e, 0x22, 0x79, 0x0a, 0x15, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, + 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x54, 0x61, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x22, 0x31, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x22, + 0x6e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, + 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0x47, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, + 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x4b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, - 0x7d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3c, - 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x99, 0x01, 0x0a, - 0x14, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x43, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, - 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa4, 0x01, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, - 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x65, 0x6e, 0x64, 0x22, 0x88, 0x01, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x64, 0x65, 0x49, 0x64, 0x22, - 0xa7, 0x01, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x06, - 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x1d, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xd8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x60, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x1a, 0x5a, 0x0a, 0x14, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x97, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, - 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, 0x6e, - 0x63, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xe6, 0x02, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, - 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x15, 0x0a, 0x06, 0x75, 0x73, - 0x65, 0x5f, 0x64, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x75, 0x73, 0x65, 0x44, - 0x62, 0x12, 0x37, 0x0a, 0x18, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, - 0x67, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x15, 0x66, 0x69, 0x6c, 0x6c, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x57, 0x69, 0x74, 0x68, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x22, 0xce, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, - 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x70, 0x61, 0x69, + 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x80, 0x01, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, + 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a, + 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x7d, + 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, + 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3c, 0x0a, + 0x1e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, - 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x22, 0x84, 0x01, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6c, - 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, - 0x6f, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0a, 0x41, 0x75, 0x64, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x22, 0x62, 0x0a, 0x09, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, - 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, - 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, - 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x41, 0x0a, 0x14, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, - 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, - 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xa8, 0x01, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, - 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x47, 0x0a, 0x1a, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x22, 0x42, 0x0a, 0x15, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, - 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x4a, 0x0a, 0x18, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x5e, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x22, 0x3d, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x22, 0x63, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x50, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, + 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, + 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x43, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x75, + 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa4, 0x01, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x23, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x36, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x35, 0x0a, 0x17, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x22, 0x93, 0x02, 0x0a, 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, + 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x65, 0x6e, 0x64, 0x22, 0x88, 0x01, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x64, 0x65, 0x49, 0x64, 0x22, 0xa7, + 0x01, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x06, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, + 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x79, 0x6e, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, 0x6e, 0x63, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xe6, 0x02, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x15, 0x0a, 0x06, 0x75, 0x73, 0x65, + 0x5f, 0x64, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x75, 0x73, 0x65, 0x44, 0x62, + 0x12, 0x37, 0x0a, 0x18, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x15, 0x66, 0x69, 0x6c, 0x6c, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x57, + 0x69, 0x74, 0x68, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, + 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, + 0xce, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x37, 0x0a, 0x17, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x61, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x55, - 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x50, 0x0a, 0x1a, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, + 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x22, 0x84, 0x01, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6c, 0x6f, + 0x77, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0a, 0x41, 0x75, 0x64, 0x69, 0x74, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x22, 0x62, 0x0a, 0x09, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x55, + 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, + 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x78, 0x74, 0x52, 0x75, 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x41, 0x0a, 0x14, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x19, + 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, + 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa8, + 0x01, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x1a, 0x0a, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6f, + 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x47, 0x0a, 0x1a, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x22, 0x42, 0x0a, 0x15, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x4a, 0x0a, 0x18, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x5e, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x22, 0x3d, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x63, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x50, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x3e, 0x0a, 0x20, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x23, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x36, 0x0a, 0x18, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x35, 0x0a, 0x17, 0x57, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x22, 0x93, 0x02, 0x0a, 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x21, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x37, 0x0a, 0x17, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x16, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, 0x6c, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x55, 0x72, + 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x50, 0x0a, 0x1a, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x3e, 0x0a, 0x20, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, - 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, - 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x46, 0x0a, 0x16, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x75, 0x72, 0x6c, 0x22, 0xd3, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x1e, 0x46, 0x69, - 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, - 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, - 0x6e, 0x64, 0x22, 0xcd, 0x01, 0x0a, 0x1c, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, - 0x69, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x57, 0x0a, 0x21, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xa3, 0x06, 0x0a, 0x1b, - 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, - 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, - 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, - 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, - 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x4f, 0x6e, - 0x6c, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, - 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, - 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x19, 0x70, - 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, - 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, - 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, - 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x63, 0x69, 0x6d, - 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, - 0x73, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x64, 0x65, 0x63, 0x69, 0x6d, - 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, - 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x73, 0x73, - 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, - 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x69, - 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x65, 0x72, - 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, - 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4f, 0x6e, 0x49, 0x73, 0x73, 0x75, - 0x65, 0x22, 0x56, 0x0a, 0x1b, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x37, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x58, 0x0a, 0x1c, 0x49, 0x6e, 0x73, - 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, 0x62, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6a, 0x6f, 0x62, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x6a, - 0x6f, 0x62, 0x73, 0x22, 0x4f, 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x15, 0x0a, - 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, - 0x6f, 0x62, 0x49, 0x64, 0x22, 0x6f, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x87, 0x07, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x05, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x21, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, 0x18, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x46, 0x0a, 0x16, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6c, 0x22, 0xd3, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, + 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, + 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, + 0x64, 0x22, 0xcd, 0x01, 0x0a, 0x1c, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, - 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, 0x72, 0x77, - 0x72, 0x69, 0x74, 0x65, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, - 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, - 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x64, - 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x64, - 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, - 0x72, 0x79, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, - 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, - 0x1a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, - 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x18, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, - 0x65, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, - 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4f, 0x6e, - 0x49, 0x73, 0x73, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x6a, 0x6f, 0x62, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x22, - 0xa0, 0x01, 0x0a, 0x14, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, - 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6e, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x44, 0x61, - 0x74, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x07, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x5c, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, - 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, - 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, - 0x64, 0x44, 0x61, 0x74, 0x65, 0x22, 0x64, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x22, 0x57, 0x0a, 0x21, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xa3, 0x06, 0x0a, 0x1b, 0x55, + 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, + 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, + 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x7a, + 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, + 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x4f, 0x6e, 0x6c, + 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, + 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, + 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x78, + 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, + 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, + 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, + 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, + 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, + 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, + 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x73, 0x73, 0x75, + 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x63, + 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x69, 0x73, + 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x65, 0x72, 0x63, + 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4f, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, + 0x22, 0x56, 0x0a, 0x1b, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x37, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x58, 0x0a, 0x1c, 0x49, 0x6e, 0x73, 0x65, + 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, 0x62, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x6a, 0x6f, + 0x62, 0x73, 0x22, 0x4f, 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x15, 0x0a, 0x06, + 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, + 0x62, 0x49, 0x64, 0x22, 0x6f, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x66, 0x75, 0x6c, 0x6c, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x22, 0x87, 0x07, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x27, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, - 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0xb9, 0x01, 0x0a, 0x12, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x41, 0x0a, 0x13, 0x4d, - 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, - 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, 0x38, - 0x0a, 0x1a, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, - 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x63, 0x0a, 0x1b, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x67, 0x0a, - 0x1f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, - 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x64, 0x0a, 0x1c, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, + 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, + 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3a, + 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, + 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, + 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, + 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x64, 0x65, + 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x69, 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x1a, + 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x5f, + 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x18, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, + 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, + 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x18, 0x14, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4f, 0x6e, 0x49, + 0x73, 0x73, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x6a, 0x6f, 0x62, 0x5f, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x6a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa0, + 0x01, 0x0a, 0x14, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6e, 0x5f, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x44, 0x61, 0x74, + 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x07, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x5c, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, + 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, + 0x44, 0x61, 0x74, 0x65, 0x22, 0x64, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x27, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, + 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, + 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xb9, + 0x01, 0x0a, 0x12, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x41, 0x0a, 0x13, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, 0x38, 0x0a, + 0x1a, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x63, 0x0a, 0x1b, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x63, 0x0a, 0x1b, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x22, 0x57, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0xbe, 0x01, 0x0a, 0x0d, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x29, - 0x0a, 0x10, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa8, 0x02, 0x0a, 0x1a, - 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x67, 0x0a, 0x1f, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, + 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, + 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x64, 0x0a, 0x1c, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x63, 0x0a, 0x1b, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, - 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, - 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, - 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x22, 0x57, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0xbe, 0x01, 0x0a, 0x0d, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x29, 0x0a, + 0x10, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa9, 0x02, 0x0a, 0x1a, 0x47, + 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, + 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, - 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x8d, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x91, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, - 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, - 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, - 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, - 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x4e, 0x4c, - 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, - 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, - 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, - 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, - 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, - 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, - 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, - 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, - 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, - 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, - 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, - 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xee, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, - 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2a, - 0x0a, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, - 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, - 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5a, 0x65, 0x72, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5a, 0x65, - 0x72, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x4b, 0x0a, 0x11, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, - 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xc9, 0x01, 0x0a, 0x15, 0x43, 0x6f, - 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, - 0x26, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, - 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, - 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, - 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, - 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, + 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x6c, + 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, + 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x50, 0x4e, 0x4c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x50, 0x4e, 0x4c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x91, 0x02, 0x0a, 0x0e, 0x46, + 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, + 0x50, 0x4e, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, + 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, + 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, + 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, + 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xf3, + 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, + 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, + 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x6c, 0x63, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, + 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, + 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5a, 0x65, 0x72, 0x6f, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x22, 0xbd, 0x05, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, + 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x12, 0x4f, 0x0a, 0x25, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, + 0x6f, 0x66, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x6f, 0x74, + 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x66, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x6f, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x73, 0x12, 0x65, 0x0a, 0x30, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x2b, 0x63, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x6f, 0x74, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x64, + 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x64, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, + 0x6c, 0x12, 0x46, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, + 0x6f, 0x77, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, + 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, + 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x31, 0x0a, 0x14, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, + 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x35, 0x0a, 0x16, + 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, + 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6d, 0x61, + 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, + 0x64, 0x50, 0x4e, 0x4c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, + 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, 0x12, 0x4c, 0x0a, 0x12, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, + 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x4b, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0b, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x11, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x65, 0x61, 0x6b, + 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xf7, 0x04, 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, + 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x75, + 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x41, + 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x18, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x78, 0x5f, 0x66, 0x61, 0x69, 0x72, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, + 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x78, 0x46, 0x61, 0x69, 0x72, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, + 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x69, + 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x17, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x16, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, + 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, + 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x72, + 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x50, 0x4e, 0x4c, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x4e, 0x4c, + 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75, 0x73, 0x65, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x49, 0x6e, 0x55, + 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x64, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, + 0x12, 0x46, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, + 0x77, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, + 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x42, + 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x8f, + 0x02, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x79, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, + 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, + 0x69, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, + 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x15, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, + 0x22, 0xae, 0x03, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, + 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x28, 0x0a, 0x10, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, + 0x53, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, + 0x5f, 0x69, 0x6e, 0x5f, 0x4e, 0x46, 0x54, 0x5f, 0x62, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x4e, 0x46, 0x54, 0x42, + 0x69, 0x64, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x46, 0x65, 0x65, 0x56, + 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x24, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, + 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x5f, + 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x1f, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, + 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, + 0x66, 0x66, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, + 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x73, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x73, + 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x49, 0x6e, 0x46, 0x75, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, + 0x6f, 0x74, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x75, 0x73, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, + 0x6e, 0x32, 0x83, 0x59, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, + 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, + 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, + 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, + 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, + 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, + 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6a, 0x0a, 0x10, - 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x73, - 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, - 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x72, 0x70, 0x63, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x18, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, - 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, - 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, - 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, + 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, - 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, - 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, - 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, - 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, - 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, - 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, - 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, - 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, - 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, - 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, - 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, - 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, - 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, + 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, - 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, - 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, - 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, - 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, - 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, - 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, - 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, + 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, + 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, + 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, + 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, - 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, - 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, - 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, - 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, - 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, - 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, - 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, - 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, + 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, + 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, + 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, + 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, + 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, + 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, + 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, + 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, + 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, + 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, + 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, + 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, + 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, + 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, + 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, + 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, + 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, - 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, - 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, - 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, - 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x97, 0x01, 0x0a, + 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, + 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, + 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, + 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, + 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, + 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, + 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x6a, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, + 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, - 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, - 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, - 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, + 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, + 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, + 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, + 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, + 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, + 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, - 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, + 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, - 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, - 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, - 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, - 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, - 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, - 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, - 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, - 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, - 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x73, + 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, + 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, 0x20, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, - 0x10, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, + 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, - 0x6c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, - 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, - 0x2b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, - 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, - 0x77, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x97, + 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, - 0x13, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, - 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, - 0x74, 0x75, 0x72, 0x6c, 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, - 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, - 0x6d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, - 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, - 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, + 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, + 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, + 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, 0x6c, + 0x12, 0x69, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, + 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, - 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, - 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, - 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, - 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, - 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, - 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, - 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, - 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, - 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, - 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, - 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, - 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, - 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, + 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, + 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, + 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, + 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, + 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, + 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, + 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, + 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, + 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, + 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, + 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, + 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, + 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, + 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, + 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, + 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, - 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, - 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, - 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, - 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, - 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, - 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, - 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, - 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, + 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, + 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, + 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, + 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, + 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, + 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, - 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, - 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, + 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, + 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, + 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, + 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, + 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, + 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, + 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, + 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, + 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, - 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, + 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, + 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, + 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -13650,7 +14111,7 @@ func file_rpc_proto_rawDescGZIP() []byte { return file_rpc_proto_rawDescData } -var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 191) +var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 193) var file_rpc_proto_goTypes = []interface{}{ (*GetInfoRequest)(nil), // 0: gctrpc.GetInfoRequest (*GetInfoResponse)(nil), // 1: gctrpc.GetInfoResponse @@ -13827,32 +14288,34 @@ var file_rpc_proto_goTypes = []interface{}{ (*GetCollateralRequest)(nil), // 172: gctrpc.GetCollateralRequest (*GetCollateralResponse)(nil), // 173: gctrpc.GetCollateralResponse (*CollateralForCurrency)(nil), // 174: gctrpc.CollateralForCurrency - nil, // 175: gctrpc.GetInfoResponse.SubsystemStatusEntry - nil, // 176: gctrpc.GetInfoResponse.RpcEndpointsEntry - nil, // 177: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - nil, // 178: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - nil, // 179: gctrpc.GetRPCEndpointsResponse.EndpointsEntry - nil, // 180: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - nil, // 181: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - nil, // 182: gctrpc.OnlineCoins.CoinsEntry - nil, // 183: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - nil, // 184: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - (*CancelBatchOrdersResponse_Orders)(nil), // 185: gctrpc.CancelBatchOrdersResponse.Orders - nil, // 186: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - (*CancelAllOrdersResponse_Orders)(nil), // 187: gctrpc.CancelAllOrdersResponse.Orders - nil, // 188: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - nil, // 189: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - nil, // 190: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - (*timestamppb.Timestamp)(nil), // 191: google.protobuf.Timestamp + (*CollateralByPosition)(nil), // 175: gctrpc.CollateralByPosition + (*CollateralUsedBreakdown)(nil), // 176: gctrpc.CollateralUsedBreakdown + nil, // 177: gctrpc.GetInfoResponse.SubsystemStatusEntry + nil, // 178: gctrpc.GetInfoResponse.RpcEndpointsEntry + nil, // 179: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + nil, // 180: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + nil, // 181: gctrpc.GetRPCEndpointsResponse.EndpointsEntry + nil, // 182: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + nil, // 183: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + nil, // 184: gctrpc.OnlineCoins.CoinsEntry + nil, // 185: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + nil, // 186: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + (*CancelBatchOrdersResponse_Orders)(nil), // 187: gctrpc.CancelBatchOrdersResponse.Orders + nil, // 188: gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + (*CancelAllOrdersResponse_Orders)(nil), // 189: gctrpc.CancelAllOrdersResponse.Orders + nil, // 190: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + nil, // 191: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + nil, // 192: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + (*timestamppb.Timestamp)(nil), // 193: google.protobuf.Timestamp } var file_rpc_proto_depIdxs = []int32{ - 175, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry - 176, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry - 177, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - 178, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - 179, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry - 180, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - 181, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + 177, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry + 178, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry + 179, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + 180, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + 181, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry + 182, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + 183, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry 21, // 7: gctrpc.GetTickerRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 8: gctrpc.TickerResponse.pair:type_name -> gctrpc.CurrencyPair 22, // 9: gctrpc.Tickers.tickers:type_name -> gctrpc.TickerResponse @@ -13867,12 +14330,12 @@ var file_rpc_proto_depIdxs = []int32{ 33, // 18: gctrpc.GetAccountInfoResponse.accounts:type_name -> gctrpc.Account 38, // 19: gctrpc.GetPortfolioResponse.portfolio:type_name -> gctrpc.PortfolioAddress 43, // 20: gctrpc.OfflineCoins.addresses:type_name -> gctrpc.OfflineCoinSummary - 182, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry + 184, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry 42, // 22: gctrpc.GetPortfolioSummaryResponse.coin_totals:type_name -> gctrpc.Coin 42, // 23: gctrpc.GetPortfolioSummaryResponse.coins_offline:type_name -> gctrpc.Coin - 183, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + 185, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry 42, // 25: gctrpc.GetPortfolioSummaryResponse.coins_online:type_name -> gctrpc.Coin - 184, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + 186, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry 51, // 27: gctrpc.GetForexProvidersResponse.forex_providers:type_name -> gctrpc.ForexProvider 54, // 28: gctrpc.GetForexRatesResponse.forex_rates:type_name -> gctrpc.ForexRatesConversion 57, // 29: gctrpc.OrderDetails.trades:type_name -> gctrpc.TradeHistory @@ -13886,23 +14349,23 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 37: gctrpc.WhaleBombRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 38: gctrpc.CancelOrderRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 39: gctrpc.CancelBatchOrdersRequest.pair:type_name -> gctrpc.CurrencyPair - 185, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders - 187, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders + 187, // 40: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.CancelBatchOrdersResponse.Orders + 189, // 41: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders 73, // 42: gctrpc.GetEventsResponse.condition_params:type_name -> gctrpc.ConditionParams 21, // 43: gctrpc.GetEventsResponse.pair:type_name -> gctrpc.CurrencyPair 73, // 44: gctrpc.AddEventRequest.condition_params:type_name -> gctrpc.ConditionParams 21, // 45: gctrpc.AddEventRequest.pair:type_name -> gctrpc.CurrencyPair 79, // 46: gctrpc.DepositAddresses.addresses:type_name -> gctrpc.DepositAddress - 189, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + 191, // 47: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry 94, // 48: gctrpc.WithdrawalEventByIDResponse.event:type_name -> gctrpc.WithdrawalEventResponse 94, // 49: gctrpc.WithdrawalEventsByExchangeResponse.event:type_name -> gctrpc.WithdrawalEventResponse 95, // 50: gctrpc.WithdrawalEventResponse.exchange:type_name -> gctrpc.WithdrawlExchangeEvent 96, // 51: gctrpc.WithdrawalEventResponse.request:type_name -> gctrpc.WithdrawalRequestEvent - 191, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp - 191, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp + 193, // 52: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp + 193, // 53: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp 97, // 54: gctrpc.WithdrawalRequestEvent.fiat:type_name -> gctrpc.FiatWithdrawalEvent 98, // 55: gctrpc.WithdrawalRequestEvent.crypto:type_name -> gctrpc.CryptoWithdrawalEvent - 190, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + 192, // 56: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry 21, // 57: gctrpc.SetExchangePairRequest.pairs:type_name -> gctrpc.CurrencyPair 21, // 58: gctrpc.GetOrderbookStreamRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 59: gctrpc.GetTickerStreamRequest.pair:type_name -> gctrpc.CurrencyPair @@ -13935,215 +14398,218 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 86: gctrpc.GetFuturesPositionsRequest.pair:type_name -> gctrpc.CurrencyPair 171, // 87: gctrpc.GetFuturesPositionsResponse.positions:type_name -> gctrpc.FuturePosition 56, // 88: gctrpc.FuturePosition.orders:type_name -> gctrpc.OrderDetails - 174, // 89: gctrpc.GetCollateralResponse.currencyBreakdown:type_name -> gctrpc.CollateralForCurrency - 9, // 90: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 3, // 91: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer - 9, // 92: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 18, // 93: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 44, // 94: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary - 45, // 95: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins - 46, // 96: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins - 186, // 97: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry - 188, // 98: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - 80, // 99: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses - 18, // 100: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 0, // 101: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest - 6, // 102: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest - 5, // 103: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 5, // 104: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 8, // 105: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest - 2, // 106: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest - 12, // 107: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest - 11, // 108: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 109: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 110: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest - 15, // 111: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest - 11, // 112: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 20, // 113: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest - 23, // 114: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest - 26, // 115: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest - 29, // 116: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest - 32, // 117: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 118: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 119: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest - 36, // 120: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest - 39, // 121: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest - 41, // 122: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest - 48, // 123: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest - 49, // 124: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest - 50, // 125: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest - 53, // 126: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest - 58, // 127: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest - 60, // 128: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest - 61, // 129: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest - 64, // 130: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest - 66, // 131: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest - 67, // 132: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest - 68, // 133: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest - 70, // 134: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest - 72, // 135: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest - 75, // 136: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest - 77, // 137: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest - 78, // 138: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest - 82, // 139: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest - 84, // 140: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest - 86, // 141: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest - 87, // 142: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest - 89, // 143: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest - 91, // 144: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest - 92, // 145: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest - 99, // 146: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest - 101, // 147: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest - 102, // 148: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest - 104, // 149: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest - 105, // 150: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest - 106, // 151: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest - 107, // 152: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest - 108, // 153: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest - 109, // 154: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest - 120, // 155: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest - 125, // 156: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest - 126, // 157: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest - 123, // 158: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest - 127, // 159: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest - 121, // 160: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest - 122, // 161: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest - 124, // 162: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest - 128, // 163: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest - 115, // 164: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest - 132, // 165: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest - 133, // 166: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest - 134, // 167: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest - 135, // 168: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest - 137, // 169: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest - 139, // 170: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest - 140, // 171: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest - 143, // 172: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest - 144, // 173: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest - 111, // 174: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 175: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest - 111, // 176: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest - 114, // 177: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest - 145, // 178: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest - 146, // 179: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest - 148, // 180: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest - 149, // 181: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest - 153, // 182: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 0, // 183: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest - 157, // 184: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest - 153, // 185: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 158, // 186: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest - 159, // 187: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest - 58, // 188: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest - 160, // 189: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest - 162, // 190: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest - 163, // 191: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest - 166, // 192: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest - 165, // 193: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest - 164, // 194: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest - 169, // 195: gctrpc.GoCryptoTrader.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest - 172, // 196: gctrpc.GoCryptoTrader.GetCollateral:input_type -> gctrpc.GetCollateralRequest - 1, // 197: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse - 7, // 198: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse - 131, // 199: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse - 131, // 200: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse - 10, // 201: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse - 4, // 202: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse - 13, // 203: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse - 131, // 204: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse - 19, // 205: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse - 14, // 206: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse - 16, // 207: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse - 131, // 208: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse - 22, // 209: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse - 25, // 210: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse - 28, // 211: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse - 31, // 212: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse - 35, // 213: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 214: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 215: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse - 37, // 216: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse - 40, // 217: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse - 47, // 218: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse - 131, // 219: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse - 131, // 220: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse - 52, // 221: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse - 55, // 222: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse - 59, // 223: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse - 56, // 224: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails - 63, // 225: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse - 65, // 226: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse - 65, // 227: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse - 131, // 228: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse - 69, // 229: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse - 71, // 230: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse - 74, // 231: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse - 76, // 232: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse - 131, // 233: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse - 81, // 234: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse - 83, // 235: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse - 85, // 236: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse - 88, // 237: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse - 88, // 238: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse - 90, // 239: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse - 93, // 240: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 93, // 241: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 100, // 242: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 100, // 243: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 103, // 244: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse - 131, // 245: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse - 28, // 246: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse - 28, // 247: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse - 22, // 248: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse - 22, // 249: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse - 110, // 250: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse - 131, // 251: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse - 131, // 252: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse - 130, // 253: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse - 129, // 254: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse - 130, // 255: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse - 131, // 256: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse - 131, // 257: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse - 129, // 258: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse - 131, // 259: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse - 116, // 260: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 131, // 261: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse - 131, // 262: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse - 131, // 263: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse - 136, // 264: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse - 138, // 265: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse - 131, // 266: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse - 142, // 267: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse - 131, // 268: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse - 131, // 269: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse - 113, // 270: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 271: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse - 113, // 272: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse - 116, // 273: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 147, // 274: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 147, // 275: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 131, // 276: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse - 152, // 277: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse - 154, // 278: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob - 156, // 279: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs - 156, // 280: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs - 154, // 281: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob - 131, // 282: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse - 131, // 283: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse - 59, // 284: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse - 161, // 285: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse - 167, // 286: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse - 131, // 287: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse - 131, // 288: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse - 131, // 289: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse - 131, // 290: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse - 170, // 291: gctrpc.GoCryptoTrader.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse - 173, // 292: gctrpc.GoCryptoTrader.GetCollateral:output_type -> gctrpc.GetCollateralResponse - 197, // [197:293] is the sub-list for method output_type - 101, // [101:197] is the sub-list for method input_type - 101, // [101:101] is the sub-list for extension type_name - 101, // [101:101] is the sub-list for extension extendee - 0, // [0:101] is the sub-list for field type_name + 176, // 89: gctrpc.GetCollateralResponse.used_breakdown:type_name -> gctrpc.CollateralUsedBreakdown + 174, // 90: gctrpc.GetCollateralResponse.currency_breakdown:type_name -> gctrpc.CollateralForCurrency + 175, // 91: gctrpc.GetCollateralResponse.position_breakdown:type_name -> gctrpc.CollateralByPosition + 176, // 92: gctrpc.CollateralForCurrency.used_breakdown:type_name -> gctrpc.CollateralUsedBreakdown + 9, // 93: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 3, // 94: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer + 9, // 95: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 18, // 96: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 44, // 97: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary + 45, // 98: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins + 46, // 99: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins + 188, // 100: gctrpc.CancelBatchOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelBatchOrdersResponse.Orders.OrderStatusEntry + 190, // 101: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry + 80, // 102: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses + 18, // 103: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 0, // 104: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest + 6, // 105: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest + 5, // 106: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 5, // 107: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 8, // 108: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest + 2, // 109: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest + 12, // 110: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest + 11, // 111: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 112: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 113: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest + 15, // 114: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest + 11, // 115: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 20, // 116: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest + 23, // 117: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest + 26, // 118: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest + 29, // 119: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest + 32, // 120: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 121: gctrpc.GoCryptoTrader.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 122: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest + 36, // 123: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest + 39, // 124: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest + 41, // 125: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest + 48, // 126: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest + 49, // 127: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest + 50, // 128: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest + 53, // 129: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest + 58, // 130: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest + 60, // 131: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest + 61, // 132: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest + 64, // 133: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest + 66, // 134: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest + 67, // 135: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest + 68, // 136: gctrpc.GoCryptoTrader.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest + 70, // 137: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest + 72, // 138: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest + 75, // 139: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest + 77, // 140: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest + 78, // 141: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest + 82, // 142: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest + 84, // 143: gctrpc.GoCryptoTrader.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest + 86, // 144: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest + 87, // 145: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest + 89, // 146: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest + 91, // 147: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest + 92, // 148: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest + 99, // 149: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest + 101, // 150: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest + 102, // 151: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest + 104, // 152: gctrpc.GoCryptoTrader.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest + 105, // 153: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest + 106, // 154: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest + 107, // 155: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest + 108, // 156: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest + 109, // 157: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest + 120, // 158: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest + 125, // 159: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest + 126, // 160: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest + 123, // 161: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest + 127, // 162: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest + 121, // 163: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest + 122, // 164: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest + 124, // 165: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest + 128, // 166: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest + 115, // 167: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest + 132, // 168: gctrpc.GoCryptoTrader.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest + 133, // 169: gctrpc.GoCryptoTrader.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest + 134, // 170: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest + 135, // 171: gctrpc.GoCryptoTrader.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest + 137, // 172: gctrpc.GoCryptoTrader.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest + 139, // 173: gctrpc.GoCryptoTrader.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest + 140, // 174: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest + 143, // 175: gctrpc.GoCryptoTrader.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest + 144, // 176: gctrpc.GoCryptoTrader.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest + 111, // 177: gctrpc.GoCryptoTrader.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 178: gctrpc.GoCryptoTrader.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest + 111, // 179: gctrpc.GoCryptoTrader.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest + 114, // 180: gctrpc.GoCryptoTrader.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest + 145, // 181: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest + 146, // 182: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest + 148, // 183: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest + 149, // 184: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest + 153, // 185: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 0, // 186: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest + 157, // 187: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest + 153, // 188: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 158, // 189: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest + 159, // 190: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest + 58, // 191: gctrpc.GoCryptoTrader.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest + 160, // 192: gctrpc.GoCryptoTrader.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest + 162, // 193: gctrpc.GoCryptoTrader.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest + 163, // 194: gctrpc.GoCryptoTrader.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest + 166, // 195: gctrpc.GoCryptoTrader.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest + 165, // 196: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest + 164, // 197: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest + 169, // 198: gctrpc.GoCryptoTrader.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest + 172, // 199: gctrpc.GoCryptoTrader.GetCollateral:input_type -> gctrpc.GetCollateralRequest + 1, // 200: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse + 7, // 201: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse + 131, // 202: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericResponse + 131, // 203: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericResponse + 10, // 204: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse + 4, // 205: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse + 13, // 206: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse + 131, // 207: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericResponse + 19, // 208: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse + 14, // 209: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse + 16, // 210: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse + 131, // 211: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericResponse + 22, // 212: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse + 25, // 213: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse + 28, // 214: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse + 31, // 215: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse + 35, // 216: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 217: gctrpc.GoCryptoTrader.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 218: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse + 37, // 219: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse + 40, // 220: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse + 47, // 221: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse + 131, // 222: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.GenericResponse + 131, // 223: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse + 52, // 224: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse + 55, // 225: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse + 59, // 226: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse + 56, // 227: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails + 63, // 228: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse + 65, // 229: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse + 65, // 230: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse + 131, // 231: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.GenericResponse + 69, // 232: gctrpc.GoCryptoTrader.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse + 71, // 233: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse + 74, // 234: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse + 76, // 235: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse + 131, // 236: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.GenericResponse + 81, // 237: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse + 83, // 238: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse + 85, // 239: gctrpc.GoCryptoTrader.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse + 88, // 240: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse + 88, // 241: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse + 90, // 242: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse + 93, // 243: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 93, // 244: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 100, // 245: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 100, // 246: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 103, // 247: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse + 131, // 248: gctrpc.GoCryptoTrader.SetExchangePair:output_type -> gctrpc.GenericResponse + 28, // 249: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse + 28, // 250: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse + 22, // 251: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse + 22, // 252: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse + 110, // 253: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse + 131, // 254: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GenericResponse + 131, // 255: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GenericResponse + 130, // 256: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse + 129, // 257: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse + 130, // 258: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse + 131, // 259: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GenericResponse + 131, // 260: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GenericResponse + 129, // 261: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse + 131, // 262: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse + 116, // 263: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 131, // 264: gctrpc.GoCryptoTrader.SetExchangeAsset:output_type -> gctrpc.GenericResponse + 131, // 265: gctrpc.GoCryptoTrader.SetAllExchangePairs:output_type -> gctrpc.GenericResponse + 131, // 266: gctrpc.GoCryptoTrader.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse + 136, // 267: gctrpc.GoCryptoTrader.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse + 138, // 268: gctrpc.GoCryptoTrader.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse + 131, // 269: gctrpc.GoCryptoTrader.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse + 142, // 270: gctrpc.GoCryptoTrader.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse + 131, // 271: gctrpc.GoCryptoTrader.WebsocketSetProxy:output_type -> gctrpc.GenericResponse + 131, // 272: gctrpc.GoCryptoTrader.WebsocketSetURL:output_type -> gctrpc.GenericResponse + 113, // 273: gctrpc.GoCryptoTrader.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 274: gctrpc.GoCryptoTrader.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse + 113, // 275: gctrpc.GoCryptoTrader.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse + 116, // 276: gctrpc.GoCryptoTrader.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 147, // 277: gctrpc.GoCryptoTrader.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 147, // 278: gctrpc.GoCryptoTrader.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 131, // 279: gctrpc.GoCryptoTrader.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse + 152, // 280: gctrpc.GoCryptoTrader.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse + 154, // 281: gctrpc.GoCryptoTrader.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob + 156, // 282: gctrpc.GoCryptoTrader.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs + 156, // 283: gctrpc.GoCryptoTrader.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs + 154, // 284: gctrpc.GoCryptoTrader.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob + 131, // 285: gctrpc.GoCryptoTrader.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse + 131, // 286: gctrpc.GoCryptoTrader.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse + 59, // 287: gctrpc.GoCryptoTrader.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse + 161, // 288: gctrpc.GoCryptoTrader.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse + 167, // 289: gctrpc.GoCryptoTrader.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse + 131, // 290: gctrpc.GoCryptoTrader.CurrencyStateTrading:output_type -> gctrpc.GenericResponse + 131, // 291: gctrpc.GoCryptoTrader.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse + 131, // 292: gctrpc.GoCryptoTrader.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse + 131, // 293: gctrpc.GoCryptoTrader.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse + 170, // 294: gctrpc.GoCryptoTrader.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse + 173, // 295: gctrpc.GoCryptoTrader.GetCollateral:output_type -> gctrpc.GetCollateralResponse + 200, // [200:296] is the sub-list for method output_type + 104, // [104:200] is the sub-list for method input_type + 104, // [104:104] is the sub-list for extension type_name + 104, // [104:104] is the sub-list for extension extendee + 0, // [0:104] is the sub-list for field type_name } func init() { file_rpc_proto_init() } @@ -16252,8 +16718,20 @@ func file_rpc_proto_init() { return nil } } - file_rpc_proto_msgTypes[185].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelBatchOrdersResponse_Orders); i { + file_rpc_proto_msgTypes[175].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CollateralByPosition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[176].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CollateralUsedBreakdown); i { case 0: return &v.state case 1: @@ -16265,6 +16743,18 @@ func file_rpc_proto_init() { } } file_rpc_proto_msgTypes[187].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelBatchOrdersResponse_Orders); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[189].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CancelAllOrdersResponse_Orders); i { case 0: return &v.state @@ -16283,7 +16773,7 @@ func file_rpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rpc_proto_rawDesc, NumEnums: 0, - NumMessages: 191, + NumMessages: 193, NumExtensions: 0, NumServices: 1, }, diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 1f5290a373e..bbf9af4e9ec 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -175,6 +175,9 @@ message AccountCurrencyInfo { string currency = 1; double total_value = 2; double hold = 3; + double free = 4; + double freeWithoutBorrow = 5; + double borrowed = 6; } message GetAccountInfoResponse { @@ -1030,51 +1033,88 @@ message GetFuturesPositionsRequest { string start_date = 4; string end_date = 5; string status = 6; - int64 positionLimit = 7; + int64 position_limit = 7; bool verbose = 8; bool overwrite = 9; } message GetFuturesPositionsResponse { - int64 totalOrders = 1; - string subAccount = 2; - string totalRealisedPNL = 3; - string totalUnrealisedPNL = 4; + int64 total_orders = 1; + string sub_account = 2; + string total_realisedPNL = 3; + string total_unrealisedPNL = 4; string totalPNL = 5; repeated FuturePosition positions = 6; } message FuturePosition { string status = 1; - string currentDirection = 2; + string current_direction = 2; string unrealisedPNL = 3; string realisedPNL = 4; - string openingDate = 5; - string closingDate = 6; + string opening_date = 5; + string closing_date = 6; repeated OrderDetails orders = 7; } message GetCollateralRequest { string exchange =1; string asset = 2; - string subAccount = 3; - bool includeBreakdown = 4; - bool calculateOffline = 5; - bool includeZeroValues = 6; + string sub_account = 3; + bool include_breakdown = 4; + bool calculate_offline = 5; + bool include_zero_values = 6; } message GetCollateralResponse { - string subAccount = 1; - string totalCollateral = 2; - repeated CollateralForCurrency currencyBreakdown = 3; + string sub_account = 1; + string collateral_currency = 2; + string total_value_of_positive_spot_balances = 3; + string collateral_contributed_by_positive_spot_balances = 4; + string used_collateral = 5; + CollateralUsedBreakdown used_breakdown = 6; + string available_collateral = 7; + string maintenance_collateral = 8; + string unrealisedPNL = 9; + repeated CollateralForCurrency currency_breakdown = 10; + repeated CollateralByPosition position_breakdown = 11; } message CollateralForCurrency { string currency = 1; - string originalAmount = 2; - string scaledCollateral = 3; - string scaledToCurrency = 4; - string error = 5; + bool excluded_from_collateral = 2; + string total_funds = 3; + string available_for_use_as_collateral = 4; + string approx_fair_market_value = 5; + string weighting = 6; + string collateral_contribution = 7; + string scaled_to_currency = 8; + string unrealised_PNL = 9; + string funds_in_use = 10; + string additional_collateral_used = 11; + CollateralUsedBreakdown used_breakdown = 12; + string error = 13; +} + +message CollateralByPosition { + string currency = 1; + string size = 2; + string open_order_size = 3; + string position_size = 4; + string mark_price = 5; + string required_margin = 6; + string total_collateral_used = 7; +} + +message CollateralUsedBreakdown { + string locked_in_stakes = 1; + string locked_in_NFT_bids = 2; + string locked_in_fee_voucher = 3; + string locked_in_spot_margin_funding_offers = 4; + string locked_in_spot_orders = 5; + string locked_as_collateral = 6; + string used_in_futures = 7; + string used_in_spot_margin = 8; } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index cb444b6ab17..3e9278cf024 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -3394,6 +3394,18 @@ "hold": { "type": "number", "format": "double" + }, + "free": { + "type": "number", + "format": "double" + }, + "freeWithoutBorrow": { + "type": "number", + "format": "double" + }, + "borrowed": { + "type": "number", + "format": "double" } } }, @@ -3628,26 +3640,105 @@ } } }, + "gctrpcCollateralByPosition": { + "type": "object", + "properties": { + "currency": { + "type": "string" + }, + "size": { + "type": "string" + }, + "openOrderSize": { + "type": "string" + }, + "positionSize": { + "type": "string" + }, + "markPrice": { + "type": "string" + }, + "requiredMargin": { + "type": "string" + }, + "totalCollateralUsed": { + "type": "string" + } + } + }, "gctrpcCollateralForCurrency": { "type": "object", "properties": { "currency": { "type": "string" }, - "originalAmount": { + "excludedFromCollateral": { + "type": "boolean" + }, + "totalFunds": { + "type": "string" + }, + "availableForUseAsCollateral": { + "type": "string" + }, + "approxFairMarketValue": { + "type": "string" + }, + "weighting": { "type": "string" }, - "scaledCollateral": { + "collateralContribution": { "type": "string" }, "scaledToCurrency": { "type": "string" }, + "unrealisedPNL": { + "type": "string" + }, + "fundsInUse": { + "type": "string" + }, + "additionalCollateralUsed": { + "type": "string" + }, + "usedBreakdown": { + "$ref": "#/definitions/gctrpcCollateralUsedBreakdown" + }, "error": { "type": "string" } } }, + "gctrpcCollateralUsedBreakdown": { + "type": "object", + "properties": { + "lockedInStakes": { + "type": "string" + }, + "lockedInNFTBids": { + "type": "string" + }, + "lockedInFeeVoucher": { + "type": "string" + }, + "lockedInSpotMarginFundingOffers": { + "type": "string" + }, + "lockedInSpotOrders": { + "type": "string" + }, + "lockedAsCollateral": { + "type": "string" + }, + "usedInFutures": { + "type": "string" + }, + "usedInSpotMargin": { + "type": "string" + } + } + }, "gctrpcCommunicationRelayer": { "type": "object", "properties": { @@ -4176,7 +4267,28 @@ "subAccount": { "type": "string" }, - "totalCollateral": { + "collateralCurrency": { + "type": "string" + }, + "totalValueOfPositiveSpotBalances": { + "type": "string" + }, + "collateralContributedByPositiveSpotBalances": { + "type": "string" + }, + "usedCollateral": { + "type": "string" + }, + "usedBreakdown": { + "$ref": "#/definitions/gctrpcCollateralUsedBreakdown" + }, + "availableCollateral": { + "type": "string" + }, + "maintenanceCollateral": { + "type": "string" + }, + "unrealisedPNL": { "type": "string" }, "currencyBreakdown": { @@ -4184,6 +4296,12 @@ "items": { "$ref": "#/definitions/gctrpcCollateralForCurrency" } + }, + "positionBreakdown": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcCollateralByPosition" + } } } }, diff --git a/gctscript/modules/gct/exchange.go b/gctscript/modules/gct/exchange.go index b8845e44560..fb1dbf3cdee 100644 --- a/gctscript/modules/gct/exchange.go +++ b/gctscript/modules/gct/exchange.go @@ -243,7 +243,7 @@ func ExchangeAccountInfo(args ...objects.Object) (objects.Object, error) { for y := range rtnValue.Accounts[x].Currencies { temp := make(map[string]objects.Object, 3) temp["name"] = &objects.String{Value: rtnValue.Accounts[x].Currencies[y].CurrencyName.String()} - temp["total"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].TotalValue} + temp["total"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].Total} temp["hold"] = &objects.Float{Value: rtnValue.Accounts[x].Currencies[y].Hold} funds.Value = append(funds.Value, &objects.Map{Value: temp}) } diff --git a/gctscript/wrappers/gct/exchange/exchange_test.go b/gctscript/wrappers/gct/exchange/exchange_test.go index 876603ff390..ebd1991356d 100644 --- a/gctscript/wrappers/gct/exchange/exchange_test.go +++ b/gctscript/wrappers/gct/exchange/exchange_test.go @@ -140,7 +140,7 @@ func TestExchange_QueryOrder(t *testing.T) { } t.Parallel() _, err := exchangeTest.QueryOrder(context.Background(), - exchName, orderID, currency.Pair{}, assetType) + exchName, orderID, currency.EMPTYPAIR, assetType) if err != nil { t.Fatal(err) } diff --git a/gctscript/wrappers/validator/validator.go b/gctscript/wrappers/validator/validator.go index 9d2e5b0c892..48a61d5685c 100644 --- a/gctscript/wrappers/validator/validator.go +++ b/gctscript/wrappers/validator/validator.go @@ -208,8 +208,8 @@ func (w Wrapper) AccountInformation(ctx context.Context, exch string, assetType AssocChain: "", }, }, - TotalValue: 100, - Hold: 0, + Total: 100, + Hold: 0, }, }, }, diff --git a/gctscript/wrappers/validator/validator_test.go b/gctscript/wrappers/validator/validator_test.go index d198d142b5f..76a28eecd21 100644 --- a/gctscript/wrappers/validator/validator_test.go +++ b/gctscript/wrappers/validator/validator_test.go @@ -99,7 +99,7 @@ func TestWrapper_CancelOrder(t *testing.T) { } _, err = testWrapper.CancelOrder(context.Background(), - exchName, orderID, currency.Pair{}, assetType) + exchName, orderID, currency.EMPTYPAIR, assetType) if err != nil { t.Error(err) } @@ -163,13 +163,13 @@ func TestWrapper_QueryOrder(t *testing.T) { t.Parallel() _, err := testWrapper.QueryOrder(context.Background(), - exchName, orderID, currency.Pair{}, assetType) + exchName, orderID, currency.EMPTYPAIR, assetType) if err != nil { t.Fatal(err) } _, err = testWrapper.QueryOrder(context.Background(), - exchError.String(), "", currency.Pair{}, assetType) + exchError.String(), "", currency.EMPTYPAIR, assetType) if err == nil { t.Fatal("expected QueryOrder to return error on invalid name") } diff --git a/go.mod b/go.mod index 9c9c3115b6b..07876a09dff 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/thrasher-corp/gocryptotrader go 1.17 require ( - github.com/d5/tengo/v2 v2.10.0 + github.com/d5/tengo/v2 v2.10.1 github.com/gofrs/uuid v4.2.0+incompatible github.com/google/go-querystring v1.1.0 github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.4.2 + github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.2 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3 github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12 github.com/lib/pq v1.10.4 github.com/mattn/go-sqlite3 v1.14.10 @@ -25,8 +25,8 @@ require ( golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d golang.org/x/time v0.0.0-20191024005414-555d28b269f0 - google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa - google.golang.org/grpc v1.43.0 + google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 + google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 ) diff --git a/go.sum b/go.sum index 0e8f6a6cbf5..1b41d4c5c56 100644 --- a/go.sum +++ b/go.sum @@ -107,8 +107,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/d5/tengo/v2 v2.10.0 h1:gR3VwfJDBlffV8WzfSNNJ7WJtWduwbTKlAu14cA2fRs= -github.com/d5/tengo/v2 v2.10.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= +github.com/d5/tengo/v2 v2.10.1 h1:Z7vmTAQfdoExNEB9kxgqxvoBBW9bf+8uYMiDyriX5HM= +github.com/d5/tengo/v2 v2.10.1/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -202,8 +202,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -234,8 +235,8 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= @@ -243,8 +244,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.2 h1:I/pwhnUln5wbMnTyRbzswA0/JxpK8sZj0aUfI3TV1So= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.2/go.mod h1:lsuH8kb4GlMdSlI4alNIBBSAt5CHJtg3i+0WuN9J5YM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3 h1:I8MsauTJQXZ8df8qJvEln0kYNc3bSapuaSsEsnFdEFU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3/go.mod h1:lZdb/YAJUSj9OqrCHs2ihjtoO3+xK3G53wTYXFWRGDo= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -833,8 +834,9 @@ google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 h1:zzNejm+EgrbLfDZ6lu9Uud2IVvHySPl8vQzf04laR5Q= +google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -862,8 +864,9 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/log/logger_setup.go b/log/logger_setup.go index 0b2d6023859..b4e64e19a23 100644 --- a/log/logger_setup.go +++ b/log/logger_setup.go @@ -180,4 +180,5 @@ func init() { OrderBook = registerNewSubLogger("ORDERBOOK") Trade = registerNewSubLogger("TRADE") Fill = registerNewSubLogger("FILL") + Currency = registerNewSubLogger("CURRENCY") } diff --git a/log/sublogger_types.go b/log/sublogger_types.go index 1328af6045f..fcdc1898bef 100644 --- a/log/sublogger_types.go +++ b/log/sublogger_types.go @@ -34,6 +34,7 @@ var ( OrderBook *SubLogger Trade *SubLogger Fill *SubLogger + Currency *SubLogger ) // SubLogger defines a sub logger can be used externally for packages wanted to diff --git a/main.go b/main.go index 9d3c9eb3c1b..402c2a42ed9 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ func main() { flag.BoolVar(&settings.EnableTickerSyncing, "tickersync", true, "enables ticker syncing for all enabled exchanges") flag.BoolVar(&settings.EnableOrderbookSyncing, "orderbooksync", true, "enables orderbook syncing for all enabled exchanges") flag.BoolVar(&settings.EnableTradeSyncing, "tradesync", false, "enables trade syncing for all enabled exchanges") - flag.IntVar(&settings.SyncWorkers, "syncworkers", engine.DefaultSyncerWorkers, "the amount of workers (goroutines) to use for syncing exchange data") + flag.IntVar(&settings.SyncWorkersCount, "syncworkers", engine.DefaultSyncerWorkers, "the amount of workers (goroutines) to use for syncing exchange data") flag.BoolVar(&settings.SyncContinuously, "synccontinuously", true, "whether to sync exchange data continuously (ticker, orderbook and trade history info") flag.DurationVar(&settings.SyncTimeoutREST, "synctimeoutrest", engine.DefaultSyncerTimeoutREST, "the amount of time before the syncer will switch from rest protocol to the streaming protocol (e.g. from REST to websocket)") @@ -73,6 +73,7 @@ func main() { // Forex provider settings flag.BoolVar(&settings.EnableCurrencyConverter, "currencyconverter", false, "overrides config and sets up foreign exchange Currency Converter") flag.BoolVar(&settings.EnableCurrencyLayer, "currencylayer", false, "overrides config and sets up foreign exchange Currency Layer") + flag.BoolVar(&settings.EnableExchangeRates, "exchangerates", false, "overrides config and sets up foreign exchange exchangeratesapi.io") flag.BoolVar(&settings.EnableFixer, "fixer", false, "overrides config and sets up foreign exchange Fixer.io") flag.BoolVar(&settings.EnableOpenExchangeRates, "openexchangerates", false, "overrides config and sets up foreign exchange Open Exchange Rates") flag.BoolVar(&settings.EnableExchangeRateHost, "exchangeratehost", false, "overrides config and sets up foreign exchange ExchangeRate.host") diff --git a/portfolio/banking/banking.go b/portfolio/banking/banking.go index 1b5171299ff..daa556fb1d3 100644 --- a/portfolio/banking/banking.go +++ b/portfolio/banking/banking.go @@ -102,7 +102,7 @@ func (b *Account) ValidateForWithdrawal(exchange string, cur currency.Code) (err err = append(err, ErrCurrencyNotSupportedByAccount) } - if cur.Upper() == currency.AUD { + if cur.Equal(currency.AUD) { if b.BSBNumber == "" { err = append(err, ErrBSBRequiredForAUD) } diff --git a/portfolio/portfolio.go b/portfolio/portfolio.go index a5b0bd556fa..f6b0c05e875 100644 --- a/portfolio/portfolio.go +++ b/portfolio/portfolio.go @@ -113,7 +113,7 @@ func (b *Base) GetAddressBalance(address, description string, coinType currency. for x := range b.Addresses { if b.Addresses[x].Address == address && b.Addresses[x].Description == description && - b.Addresses[x].CoinType == coinType { + b.Addresses[x].CoinType.Equal(coinType) { return b.Addresses[x].Balance, true } } @@ -145,7 +145,7 @@ func (b *Base) AddressExists(address string) bool { // associated with the portfolio base func (b *Base) ExchangeAddressExists(exchangeName string, coinType currency.Code) bool { for x := range b.Addresses { - if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType == coinType { + if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType.Equal(coinType) { return true } } @@ -176,7 +176,7 @@ func (b *Base) UpdateAddressBalance(address string, amount float64) { // RemoveExchangeAddress removes an exchange address from the portfolio. func (b *Base) RemoveExchangeAddress(exchangeName string, coinType currency.Code) { for x := range b.Addresses { - if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType == coinType { + if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType.Equal(coinType) { b.Addresses = append(b.Addresses[:x], b.Addresses[x+1:]...) return } @@ -187,7 +187,7 @@ func (b *Base) RemoveExchangeAddress(exchangeName string, coinType currency.Code // against correct exchangeName and coinType. func (b *Base) UpdateExchangeAddressBalance(exchangeName string, coinType currency.Code, balance float64) { for x := range b.Addresses { - if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType == coinType { + if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType.Equal(coinType) { b.Addresses[x].Balance = balance } } @@ -237,7 +237,7 @@ func (b *Base) RemoveAddress(address, description string, coinType currency.Code for x := range b.Addresses { if b.Addresses[x].Address == address && - b.Addresses[x].CoinType == coinType && + b.Addresses[x].CoinType.Equal(coinType) && b.Addresses[x].Description == description { b.Addresses = append(b.Addresses[:x], b.Addresses[x+1:]...) return nil diff --git a/portfolio/portfolio_test.go b/portfolio/portfolio_test.go index 1974c2dafcf..2a6d7cd06d4 100644 --- a/portfolio/portfolio_test.go +++ b/portfolio/portfolio_test.go @@ -174,7 +174,7 @@ func TestUpdateAddressBalance(t *testing.T) { newBase.UpdateAddressBalance("someaddress", 0.03) value := newBase.GetPortfolioSummary() - if value.Totals[0].Coin != currency.LTC && + if !value.Totals[0].Coin.Equal(currency.LTC) && value.Totals[0].Balance != 0.03 { t.Error("UpdateUpdateAddressBalance error") } @@ -245,7 +245,7 @@ func TestUpdateExchangeAddressBalance(t *testing.T) { b.UpdateExchangeAddressBalance("someaddress", currency.LTC, 0.04) value := b.GetPortfolioSummary() - if value.Totals[0].Coin != currency.LTC && value.Totals[0].Balance != 0.04 { + if !value.Totals[0].Coin.Equal(currency.LTC) && value.Totals[0].Balance != 0.04 { t.Error("incorrect portfolio balance") } } @@ -487,18 +487,18 @@ func TestGetPortfolioSummary(t *testing.T) { getTotalsVal := func(c currency.Code) Coin { for x := range value.Totals { - if value.Totals[x].Coin == c { + if value.Totals[x].Coin.Equal(c) { return value.Totals[x] } } return Coin{} } - if getTotalsVal(currency.LTC).Coin != currency.LTC { + if !getTotalsVal(currency.LTC).Coin.Equal(currency.LTC) { t.Error("mismatched currency") } - if getTotalsVal(currency.ETH).Coin == currency.LTC { + if getTotalsVal(currency.ETH).Coin.Equal(currency.LTC) { t.Error("mismatched currency") } diff --git a/portfolio/withdraw/validate.go b/portfolio/withdraw/validate.go index 99f6246383c..297cfe8ba22 100644 --- a/portfolio/withdraw/validate.go +++ b/portfolio/withdraw/validate.go @@ -23,18 +23,18 @@ func (r *Request) Validate(opt ...validate.Checker) (err error) { allErrors = append(allErrors, ErrStrAmountMustBeGreaterThanZero) } - if (r.Currency == currency.Code{}) { + if r.Currency.Equal(currency.EMPTYCODE) { allErrors = append(allErrors, ErrStrNoCurrencySet) } switch r.Type { case Fiat: - if (r.Currency != currency.Code{}) && !r.Currency.IsFiatCurrency() { + if !r.Currency.Equal(currency.EMPTYCODE) && !r.Currency.IsFiatCurrency() { allErrors = append(allErrors, ErrStrCurrencyNotFiat) } allErrors = append(allErrors, r.validateFiat()...) case Crypto: - if (r.Currency != currency.Code{}) && !r.Currency.IsCryptocurrency() { + if !r.Currency.Equal(currency.EMPTYCODE) && !r.Currency.IsCryptocurrency() { allErrors = append(allErrors, ErrStrCurrencyNotCrypto) } allErrors = append(allErrors, r.validateCrypto()...) diff --git a/portfolio/withdraw/validate_test.go b/portfolio/withdraw/validate_test.go index 0984a10cc85..7fee4a62fdf 100644 --- a/portfolio/withdraw/validate_test.go +++ b/portfolio/withdraw/validate_test.go @@ -92,17 +92,6 @@ var ( Type: Crypto, } - invalidCryptoNonWhiteListedAddressRequest = &Request{ - Exchange: "Binance", - Crypto: CryptoRequest{ - Address: testBTCAddress, - }, - Currency: currency.BTC, - Description: "Test Withdrawal", - Amount: 0.1, - Type: Crypto, - } - invalidType = &Request{ Exchange: "test", Type: Unknown, diff --git a/testdata/http_mock/binance/binance.json b/testdata/http_mock/binance/binance.json index cc51af9fdfd..0db1aa680d3 100644 --- a/testdata/http_mock/binance/binance.json +++ b/testdata/http_mock/binance/binance.json @@ -19657,17 +19657,23 @@ { "interval": "SECOND", "intervalNum": 10, - "limit": 100, + "limit": 50, "rateLimitType": "ORDERS" }, { "interval": "DAY", "intervalNum": 1, - "limit": 200000, + "limit": 160000, "rateLimitType": "ORDERS" + }, + { + "interval": "MINUTE", + "intervalNum": 5, + "limit": 6100, + "rateLimitType": "RAW_REQUESTS" } ], - "serverTime": 1611715398593, + "serverTime": 1645761040601, "symbols": [ { "baseAsset": "ETH", @@ -19676,7 +19682,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "922327.00000000", "minPrice": "0.00000100", "tickSize": "0.00000100" }, @@ -19689,8 +19695,8 @@ { "filterType": "LOT_SIZE", "maxQty": "100000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -19704,7 +19710,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2698.17321319", + "maxQty": "892.25830097", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -19760,8 +19766,8 @@ { "filterType": "LOT_SIZE", "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -19775,7 +19781,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "19345.17979861", + "maxQty": "6209.90437804", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -19819,8 +19825,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -19831,8 +19837,8 @@ { "filterType": "LOT_SIZE", "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -19846,7 +19852,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "40964.12285417", + "maxQty": "14593.83531341", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -19917,7 +19923,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17911.44588194", + "maxQty": "7543.31214732", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -19973,14 +19979,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -19988,7 +19994,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6136.08761806", + "maxQty": "7770.50559416", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20042,15 +20048,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20058,7 +20064,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48305.15463194", + "maxQty": "39579.41785962", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20121,7 +20127,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20129,7 +20135,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "345949.72430556", + "maxQty": "460358.05489923", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20183,15 +20189,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20199,7 +20205,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6251.27882639", + "maxQty": "12451.58276580", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20241,7 +20247,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000100", "tickSize": "0.00000100" }, @@ -20253,7 +20259,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -20318,8 +20324,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -20333,7 +20339,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "38287.84202778", + "maxQty": "11432.16858929", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20376,8 +20382,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -20387,15 +20393,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20403,7 +20409,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7090.05636111", + "maxQty": "7904.12528978", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20459,8 +20465,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -20474,7 +20480,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "117.70094120", + "maxQty": "190.04109012", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20530,8 +20536,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -20545,7 +20551,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2701.84519274", + "maxQty": "2342.39927192", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -20588,7 +20594,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000100", "tickSize": "0.00000100" }, @@ -20600,7 +20606,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -20652,7 +20658,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -20672,7 +20678,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20742,7 +20748,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20812,7 +20818,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20862,7 +20868,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -20882,7 +20888,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -20997,8 +21003,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -21008,9 +21014,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -21024,7 +21030,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "143773.40672917", + "maxQty": "61045.62952050", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21086,7 +21092,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -21094,7 +21100,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17978.14056250", + "maxQty": "28248.67398611", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21126,7 +21132,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "WTCETH" }, { @@ -21164,7 +21170,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "356630.73958333", + "maxQty": "222575.23419041", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21227,7 +21233,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -21235,7 +21241,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "150855.17361111", + "maxQty": "60367.38568450", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21290,8 +21296,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -21305,7 +21311,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "77246.55570833", + "maxQty": "12517.04736622", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21376,7 +21382,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9818321.95069444", + "maxQty": "10648655.13690062", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21431,8 +21437,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -21446,7 +21452,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "77319.44195139", + "maxQty": "19865.99975677", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21502,14 +21508,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -21517,7 +21523,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13392.63354861", + "maxQty": "12231.07123002", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21587,7 +21593,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "222868.80625000", + "maxQty": "83337.42321056", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21631,8 +21637,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -21650,7 +21656,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -21658,7 +21664,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43133.95208333", + "maxQty": "47661.91799861", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21790,7 +21796,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -21868,7 +21874,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "42642417.49861111", + "maxQty": "69235100.97256515", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -21900,7 +21906,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "SNGLSBTC" }, { @@ -21930,7 +21936,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22008,7 +22014,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "214425.25138889", + "maxQty": "106190.15178571", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22040,7 +22046,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BQXBTC" }, { @@ -22050,9 +22056,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "10.00000000", + "tickSize": "10.00000000" }, { "avgPriceMins": 5, @@ -22063,14 +22069,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "10.00000000", + "stepSize": "10.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22078,7 +22084,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63469.74444444", + "maxQty": "24348.24047619", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22110,7 +22116,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BQXETH" }, { @@ -22133,8 +22139,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -22148,7 +22154,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "117427.37361111", + "maxQty": "34082.23349548", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22191,7 +22197,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -22204,14 +22210,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22219,7 +22225,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "41538.28611111", + "maxQty": "18052.11146629", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22289,7 +22295,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10432455.99236111", + "maxQty": "36380417.34743589", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22303,7 +22309,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -22314,14 +22320,15 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "FUNBTC" }, { @@ -22351,7 +22358,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22359,7 +22366,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1002314.77916667", + "maxQty": "4102268.41209173", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22429,7 +22436,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6808736.23194444", + "maxQty": "228787.93050729", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22491,7 +22498,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22542,8 +22549,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -22561,7 +22568,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22569,7 +22576,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3648.78281944", + "maxQty": "3258.04725503", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22639,7 +22646,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "552745.70347222", + "maxQty": "136603.08895066", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22683,8 +22690,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -22702,7 +22709,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22710,7 +22717,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63732.63819444", + "maxQty": "46716.11744266", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22753,8 +22760,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -22765,8 +22772,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -22780,7 +22787,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "44330.41722222", + "maxQty": "37807.34781097", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22835,7 +22842,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -22843,7 +22850,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22851,7 +22858,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17077.75700000", + "maxQty": "14900.10469075", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22922,7 +22929,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "146235373.17569444", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -22984,7 +22991,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -22992,7 +22999,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3462886.23680556", + "maxQty": "4323514.19110493", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23118,7 +23125,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -23190,7 +23197,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43217.54236111", + "maxQty": "55027.18832522", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23232,7 +23239,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -23252,7 +23259,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -23315,8 +23322,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -23330,7 +23337,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "204016.50902778", + "maxQty": "21853.32633773", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23344,7 +23351,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -23355,7 +23362,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -23373,8 +23381,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -23384,15 +23392,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -23400,7 +23408,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "22346.01881944", + "maxQty": "22944.46164002", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23526,7 +23534,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -23583,8 +23591,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -23598,7 +23606,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "239171.99442361", + "maxQty": "131878.15756080", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23669,7 +23677,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2129781.91666667", + "maxQty": "779416.28422515", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23712,8 +23720,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -23723,7 +23731,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -23731,7 +23739,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -23739,7 +23747,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2667.73920139", + "maxQty": "3068.95416956", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23782,8 +23790,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -23809,7 +23817,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "53801.90227083", + "maxQty": "6221.23202223", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23880,7 +23888,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8325195.03958333", + "maxQty": "1646255.44961779", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -23942,7 +23950,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -24062,7 +24070,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -24082,7 +24090,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -24160,7 +24168,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "636212.57152778", + "maxQty": "557859.15010423", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24231,7 +24239,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3668.26901111", + "maxQty": "2065.40749548", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24294,7 +24302,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -24302,7 +24310,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "649.58800903", + "maxQty": "489.53114315", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24357,8 +24365,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -24372,7 +24380,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "37960.93958333", + "maxQty": "21328.48846421", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24443,7 +24451,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "511680.36180556", + "maxQty": "274192.31410701", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24485,7 +24493,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -24505,7 +24513,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -24583,7 +24591,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2074.58678889", + "maxQty": "1524.77621959", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24646,7 +24654,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -24654,7 +24662,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "209.64295000", + "maxQty": "408.11217164", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24724,7 +24732,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "393478.34652778", + "maxQty": "156260.97567755", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24830,7 +24838,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000100", "tickSize": "0.00000100" }, @@ -24842,7 +24850,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -24858,7 +24866,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8691.93759722", + "maxQty": "1398.59124391", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -24920,7 +24928,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -24998,7 +25006,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "116167.67013889", + "maxQty": "256432.29230769", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25030,7 +25038,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "EVXBTC" }, { @@ -25040,7 +25048,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -25060,7 +25068,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25138,7 +25146,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2053640.18958333", + "maxQty": "295074.17512161", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25200,7 +25208,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25278,7 +25286,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4267045.60902778", + "maxQty": "2028022.30854760", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25340,7 +25348,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25348,7 +25356,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "334292.52152778", + "maxQty": "738087.94788047", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25410,7 +25418,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25482,7 +25490,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61525592.45972222", + "maxQty": "27861269.34885337", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25545,7 +25553,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25553,7 +25561,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5097407.69027778", + "maxQty": "2030954.69214732", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25624,7 +25632,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "738419.22986111", + "maxQty": "188444.24739402", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25667,8 +25675,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -25686,7 +25694,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25694,7 +25702,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "119279.85486111", + "maxQty": "47557.65045170", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25749,8 +25757,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -25764,7 +25772,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "166681.45416667", + "maxQty": "62529.57255038", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -25826,7 +25834,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25896,7 +25904,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -25974,7 +25982,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5662008.75347222", + "maxQty": "1150656.81862404", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26018,8 +26026,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -26037,7 +26045,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -26045,7 +26053,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "712620.69305556", + "maxQty": "395487.31966643", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26172,7 +26180,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -26229,8 +26237,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -26244,7 +26252,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "678319.04236111", + "maxQty": "85774.82640722", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26258,7 +26266,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -26269,7 +26277,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -26287,8 +26296,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -26299,14 +26308,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -26314,7 +26323,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "176622.20833333", + "maxQty": "30762.15545517", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26384,7 +26393,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "228252.62708333", + "maxQty": "47963.54829742", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26427,7 +26436,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -26447,7 +26456,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -26498,8 +26507,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -26525,7 +26534,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11787.18448889", + "maxQty": "10210.64839819", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26588,7 +26597,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -26652,7 +26661,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -26703,8 +26712,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -26715,8 +26724,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -26856,7 +26865,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -26901,8 +26910,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -26912,9 +26921,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -26928,7 +26937,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "104890.56900694", + "maxQty": "69038.65462126", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -26971,8 +26980,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -26982,15 +26991,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -26998,7 +27007,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "41334.11311806", + "maxQty": "25843.37355584", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27030,7 +27039,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "KMDETH" }, { @@ -27060,7 +27069,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -27138,7 +27147,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "509365.20069444", + "maxQty": "803799.02361111", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27170,7 +27179,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "RCNBTC" }, { @@ -27200,7 +27209,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -27270,7 +27279,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -27348,7 +27357,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "226284.41875000", + "maxQty": "128625.82626824", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27362,7 +27371,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -27373,7 +27382,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -27410,7 +27420,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -27488,7 +27498,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "131259.74861111", + "maxQty": "195305.84743589", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27520,7 +27530,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "RDNBTC" }, { @@ -27530,7 +27540,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -27550,7 +27560,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -27620,7 +27630,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -27698,7 +27708,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4443.64904097", + "maxQty": "6399.88974079", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27761,7 +27771,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -27769,7 +27779,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "848.10967639", + "maxQty": "2848.40864767", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27832,7 +27842,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -27883,8 +27893,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -27902,7 +27912,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -27910,7 +27920,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17350.60250000", + "maxQty": "18551.05576923", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -27942,7 +27952,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "WTCBNB" }, { @@ -27980,7 +27990,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "451402.97500000", + "maxQty": "1114279.81899871", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -28012,7 +28022,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "DLTBTC" }, { @@ -28042,7 +28052,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -28120,7 +28130,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4344941.84027778", + "maxQty": "3701113.96038915", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -28182,7 +28192,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -28252,7 +28262,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -28322,7 +28332,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -28522,7 +28532,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "838161.19513889", + "maxQty": "153412.56845031", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -28566,8 +28576,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -28585,7 +28595,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -28593,7 +28603,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "68031.65486111", + "maxQty": "37376.13968033", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -28636,8 +28646,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -28648,14 +28658,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -28663,7 +28673,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "38233.52777778", + "maxQty": "51424.57364771", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -28695,7 +28705,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BATBNB" }, { @@ -28733,7 +28743,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2736281.67916667", + "maxQty": "1891193.90562110", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -28765,7 +28775,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BCPTBTC" }, { @@ -28795,7 +28805,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -28865,7 +28875,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -29005,7 +29015,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -29056,8 +29066,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -29068,8 +29078,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -29083,7 +29093,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17053.17568750", + "maxQty": "27065.89499358", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29115,7 +29125,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "GVTBTC" }, { @@ -29145,7 +29155,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -29223,7 +29233,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15455690.76736111", + "maxQty": "1537761.87532808", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29255,7 +29265,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "CDTBTC" }, { @@ -29285,7 +29295,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -29293,7 +29303,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "696438.01111111", + "maxQty": "497672.94225721", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29325,7 +29335,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "CDTETH" }, { @@ -29363,7 +29373,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "89780.22986111", + "maxQty": "48836.08200138", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29377,7 +29387,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -29388,7 +29398,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -29406,8 +29417,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -29417,15 +29428,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -29433,7 +29444,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18239.46619444", + "maxQty": "35182.33217512", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29476,8 +29487,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -29488,8 +29499,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -29503,7 +29514,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "34704.45130903", + "maxQty": "21446.80165531", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29566,7 +29577,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -29574,7 +29585,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1291.84086111", + "maxQty": "2744.05141765", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29628,7 +29639,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -29644,7 +29655,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "402098194.39681440", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29706,7 +29717,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -29784,7 +29795,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2035721.22083333", + "maxQty": "1424963.38915913", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29838,7 +29849,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -29846,7 +29857,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -29854,7 +29865,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "451243.98263889", + "maxQty": "554074.83440308", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -29886,7 +29897,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "QSPETH" }, { @@ -29916,7 +29927,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -29994,7 +30005,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3268685.81597222", + "maxQty": "5343102.38151494", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -30008,7 +30019,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -30019,7 +30030,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -30056,7 +30068,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -30126,7 +30138,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -30266,7 +30278,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -30336,7 +30348,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -30387,8 +30399,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -30399,8 +30411,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -30414,7 +30426,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "71387.62133333", + "maxQty": "35528.14378040", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -30468,15 +30480,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -30484,7 +30496,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9636.54952083", + "maxQty": "19517.87911744", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -30546,7 +30558,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -30686,7 +30698,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -30748,7 +30760,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -30764,7 +30776,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "146314151.54247911", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -30826,7 +30838,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -30904,7 +30916,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "660910.87777778", + "maxQty": "173763.08964558", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -30948,8 +30960,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -30967,7 +30979,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -30975,7 +30987,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "137070.34513889", + "maxQty": "80605.20430854", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31030,8 +31042,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -31045,7 +31057,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "54475.63680556", + "maxQty": "39785.01042390", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31107,7 +31119,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -31247,7 +31259,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -31298,8 +31310,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -31310,14 +31322,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -31325,7 +31337,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43621.53340278", + "maxQty": "38956.11605281", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31395,7 +31407,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "341380.04513889", + "maxQty": "228255.59694232", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31437,7 +31449,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -31457,7 +31469,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -31465,7 +31477,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50229.75069444", + "maxQty": "122308.23419041", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31527,7 +31539,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -31590,8 +31602,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -31605,7 +31617,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7459965.52916667", + "maxQty": "908103.66726893", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31649,8 +31661,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -31661,14 +31673,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -31676,7 +31688,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "823619.33541667", + "maxQty": "499280.08763029", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31732,8 +31744,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -31747,7 +31759,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61793.46805556", + "maxQty": "67692.10277777", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31779,7 +31791,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "PPTBTC" }, { @@ -31809,7 +31821,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -31887,7 +31899,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9766964.05138889", + "maxQty": "10813887.88888889", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31919,7 +31931,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "CMTBTC" }, { @@ -31949,7 +31961,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -31957,7 +31969,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "444009.94722222", + "maxQty": "938537.48854962", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -31989,7 +32001,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "CMTETH" }, { @@ -32019,7 +32031,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -32097,7 +32109,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2436232.22291667", + "maxQty": "2248153.29395413", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32141,8 +32153,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -32160,7 +32172,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -32168,7 +32180,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "367632.32430556", + "maxQty": "498764.67824878", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32211,8 +32223,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -32230,7 +32242,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -32238,7 +32250,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "80968.72013889", + "maxQty": "264933.09103544", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32308,7 +32320,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10369577.13611111", + "maxQty": "78945794.23141070", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32370,7 +32382,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -32440,7 +32452,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -32581,7 +32593,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -32659,7 +32671,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "497331.22083333", + "maxQty": "311734.46490618", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32721,7 +32733,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -32772,8 +32784,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -32791,7 +32803,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -32799,7 +32811,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "107629.36180556", + "maxQty": "84241.07783182", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32861,7 +32873,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -32869,7 +32881,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1564.84996181", + "maxQty": "1605.87860111", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32913,8 +32925,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -32925,8 +32937,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -32940,7 +32952,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "24345.61619556", + "maxQty": "13312.80972239", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -32984,8 +32996,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -33003,7 +33015,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -33011,7 +33023,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1098.19282986", + "maxQty": "912.76099791", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33065,7 +33077,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -33081,7 +33093,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "204369838.38541667", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33113,7 +33125,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "TNBBTC" }, { @@ -33143,7 +33155,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -33221,7 +33233,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43611.63152083", + "maxQty": "8099.76788047", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33284,7 +33296,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -33292,7 +33304,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4035.15581250", + "maxQty": "3301.29105628", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33347,14 +33359,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -33362,7 +33374,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2674.44965278", + "maxQty": "2755.07069492", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33432,7 +33444,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "14372024.75416667", + "maxQty": "2530316.50868658", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33495,7 +33507,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -33565,7 +33577,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -33643,7 +33655,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "241568.43541667", + "maxQty": "81857.48019457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33678,6 +33690,76 @@ "status": "TRADING", "symbol": "ICXBTC" }, + { + "baseAsset": "ICX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "47785.69075747", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ICXETH" + }, { "baseAsset": "ICX", "baseAssetPrecision": 8, @@ -33697,15 +33779,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -33713,7 +33795,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "34586.54162500", + "maxQty": "15202.55138888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33740,24 +33822,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ICXETH" + "status": "BREAK", + "symbol": "ICXBNB" }, { - "baseAsset": "ICX", + "baseAsset": "OST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -33767,15 +33849,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -33783,7 +33865,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26721.91923611", + "maxQty": "9211514.86282051", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33810,13 +33892,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ICXBNB" + "status": "BREAK", + "symbol": "OSTBTC" }, { "baseAsset": "OST", @@ -33845,7 +33927,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -33853,7 +33935,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6996380.10555556", + "maxQty": "1553816.20000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33880,18 +33962,88 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "OSTBTC" + "status": "BREAK", + "symbol": "OSTETH" }, { "baseAsset": "OST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1704481.68673051", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "OSTBNB" + }, + { + "baseAsset": "ELF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", @@ -33915,7 +34067,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -33923,7 +34075,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "852560.20208333", + "maxQty": "67390.26754690", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -33950,16 +34102,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OSTETH" + "symbol": "ELFBTC" }, { - "baseAsset": "OST", + "baseAsset": "ELF", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -33977,7 +34129,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -33985,7 +34137,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -33993,7 +34145,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1704481.68673051", + "maxQty": "67945.48019457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34020,16 +34172,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "OSTBNB" + "status": "TRADING", + "symbol": "ELFETH" }, { - "baseAsset": "ELF", + "baseAsset": "AION", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -34063,7 +34215,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "267387.52638889", + "maxQty": "576102.89506601", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34096,10 +34248,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ELFBTC" + "symbol": "AIONBTC" }, { - "baseAsset": "ELF", + "baseAsset": "AION", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -34117,7 +34269,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -34125,7 +34277,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -34133,7 +34285,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "78020.08611111", + "maxQty": "355727.84781097", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34166,7 +34318,7 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ELFETH" + "symbol": "AIONETH" }, { "baseAsset": "AION", @@ -34176,8 +34328,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -34187,7 +34339,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -34195,7 +34347,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -34203,7 +34355,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1658488.24583333", + "maxQty": "61789.32941176", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34230,24 +34382,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "AIONBTC" + "status": "BREAK", + "symbol": "AIONBNB" }, { - "baseAsset": "AION", + "baseAsset": "NEBL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -34258,14 +34410,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -34273,7 +34425,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "139558.85040972", + "maxQty": "41938.70898540", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34300,24 +34452,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AIONETH" + "symbol": "NEBLBTC" }, { - "baseAsset": "AION", + "baseAsset": "NEBL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -34328,14 +34480,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -34343,7 +34495,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61789.32941176", + "maxQty": "38228.32298547", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34376,10 +34528,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "AIONBNB" + "symbol": "NEBLBNB" }, { - "baseAsset": "NEBL", + "baseAsset": "BRD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -34413,7 +34565,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "51480.42569444", + "maxQty": "217290.13968033", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34446,18 +34598,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEBLBTC" + "symbol": "BRDBTC" }, { - "baseAsset": "NEBL", + "baseAsset": "BRD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -34468,14 +34620,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -34483,7 +34635,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11816.86863194", + "maxQty": "81304.78387769", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34516,10 +34668,80 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEBLETH" + "symbol": "BRDETH" }, { - "baseAsset": "NEBL", + "baseAsset": "BRD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "99411.67919556", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BRDBNB" + }, + { + "baseAsset": "MCO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -34545,7 +34767,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -34553,7 +34775,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "38228.32298547", + "maxQty": "12300.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34586,10 +34808,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "NEBLBNB" + "symbol": "MCOBNB" }, { - "baseAsset": "BRD", + "baseAsset": "EDO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -34623,7 +34845,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "387405.02083333", + "maxQty": "927073.24071577", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34655,19 +34877,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BRDBTC" + "status": "BREAK", + "symbol": "EDOBTC" }, { - "baseAsset": "BRD", + "baseAsset": "EDO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -34678,14 +34900,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -34693,7 +34915,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "111891.49652778", + "maxQty": "112098.60868659", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34725,19 +34947,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BRDETH" + "status": "BREAK", + "symbol": "EDOETH" }, { - "baseAsset": "BRD", + "baseAsset": "WINGS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -34747,26 +34969,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "99411.67919556", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -34790,24 +35006,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BRDBNB" + "symbol": "WINGSBTC" }, { - "baseAsset": "MCO", + "baseAsset": "WINGS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -34817,26 +35033,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "12300.00000000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -34860,16 +35070,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "MCOBNB" + "symbol": "WINGSETH" }, { - "baseAsset": "EDO", + "baseAsset": "NAV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -34903,7 +35113,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "927073.24071577", + "maxQty": "83131.80472550", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -34935,19 +35145,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "EDOBTC" + "status": "TRADING", + "symbol": "NAVBTC" }, { - "baseAsset": "EDO", + "baseAsset": "NAV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -34958,14 +35168,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -34973,7 +35183,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "112098.60868659", + "maxQty": "31046.17614424", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35006,18 +35216,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "EDOETH" + "symbol": "NAVETH" }, { - "baseAsset": "WINGS", + "baseAsset": "NAV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -35027,7 +35237,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -35035,12 +35245,18 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "43600.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35064,22 +35280,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "WINGSBTC" + "symbol": "NAVBNB" }, { - "baseAsset": "WINGS", + "baseAsset": "LUN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -35092,19 +35308,25 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "182477.48166551", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35128,24 +35350,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "WINGSETH" + "symbol": "LUNBTC" }, { - "baseAsset": "NAV", + "baseAsset": "LUN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -35156,14 +35378,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -35171,7 +35393,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "204795.71041667", + "maxQty": "45500.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35198,24 +35420,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "NAVBTC" + "status": "BREAK", + "symbol": "LUNETH" }, { - "baseAsset": "NAV", + "baseAsset": "TRIG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -35233,18 +35455,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "250300.00000000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35268,16 +35484,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "NAVETH" + "symbol": "TRIGBTC" }, { - "baseAsset": "NAV", + "baseAsset": "TRIG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -35295,26 +35511,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "43600.00000000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35338,24 +35548,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "NAVBNB" + "symbol": "TRIGETH" }, { - "baseAsset": "LUN", + "baseAsset": "TRIG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -35365,26 +35575,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "182477.48166551", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35408,24 +35612,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "LUNBTC" + "symbol": "TRIGBNB" }, { - "baseAsset": "LUN", + "baseAsset": "APPC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -35436,14 +35640,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -35451,7 +35655,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "45500.00000000", + "maxQty": "2023197.30769230", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35478,16 +35682,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "LUNETH" + "symbol": "APPCBTC" }, { - "baseAsset": "TRIG", + "baseAsset": "APPC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -35506,19 +35710,25 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "595557.66298343", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35542,16 +35752,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TRIGBTC" + "symbol": "APPCETH" }, { - "baseAsset": "TRIG", + "baseAsset": "APPC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -35569,20 +35779,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "638256.60789474", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35606,24 +35822,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TRIGETH" + "symbol": "APPCBNB" }, { - "baseAsset": "TRIG", + "baseAsset": "VIBE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -35633,20 +35849,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2369135.16724497", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -35670,24 +35892,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TRIGBNB" + "symbol": "VIBEBTC" }, { - "baseAsset": "APPC", + "baseAsset": "VIBE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -35705,7 +35927,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -35713,7 +35935,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "865998.98750000", + "maxQty": "1992743.80027174", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35740,24 +35962,95 @@ "permissions": [ "SPOT" ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "VIBEETH" + }, + { + "baseAsset": "RLC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "23891.14718554", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "APPCBTC" + "symbol": "RLCBTC" }, { - "baseAsset": "APPC", + "baseAsset": "RLC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -35768,14 +36061,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -35783,7 +36076,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "595557.66298343", + "maxQty": "19961.00965948", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35815,19 +36108,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "APPCETH" + "status": "TRADING", + "symbol": "RLCETH" }, { - "baseAsset": "APPC", + "baseAsset": "RLC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -35838,14 +36131,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -35853,7 +36146,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "638256.60789474", + "maxQty": "15318.45748960", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35886,10 +36179,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "APPCBNB" + "symbol": "RLCBNB" }, { - "baseAsset": "VIBE", + "baseAsset": "INS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -35923,7 +36216,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2110609.02569444", + "maxQty": "544712.46977067", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -35955,19 +36248,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "VIBEBTC" + "status": "BREAK", + "symbol": "INSBTC" }, { - "baseAsset": "VIBE", + "baseAsset": "INS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -35978,14 +36271,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -35993,7 +36286,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1992743.80027174", + "maxQty": "180847.41551105", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36026,10 +36319,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "VIBEETH" + "symbol": "INSETH" }, { - "baseAsset": "RLC", + "baseAsset": "PIVX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -36063,7 +36356,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "52341.35000000", + "maxQty": "54359.04378040", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36077,7 +36370,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -36088,8 +36381,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -36097,10 +36389,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RLCBTC" + "symbol": "PIVXBTC" }, { - "baseAsset": "RLC", + "baseAsset": "PIVX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -36118,15 +36410,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -36134,7 +36426,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13061.00365972", + "maxQty": "118907.07612551", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36161,24 +36453,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "RLCETH" + "status": "BREAK", + "symbol": "PIVXBNB" }, { - "baseAsset": "RLC", + "baseAsset": "IOST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -36188,15 +36480,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -36204,7 +36496,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15318.45748960", + "maxQty": "67243671.03752605", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36218,7 +36510,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -36229,18 +36521,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "RLCBNB" + "status": "TRADING", + "symbol": "IOSTBTC" }, { - "baseAsset": "INS", + "baseAsset": "IOST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -36266,7 +36559,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -36274,7 +36567,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "544712.46977067", + "maxQty": "1572956.01181375", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36301,24 +36594,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "INSBTC" + "status": "TRADING", + "symbol": "IOSTETH" }, { - "baseAsset": "INS", + "baseAsset": "CHAT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -36329,25 +36622,19 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "180847.41551105", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -36371,16 +36658,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "INSETH" + "symbol": "CHATBTC" }, { - "baseAsset": "PIVX", + "baseAsset": "CHAT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -36406,18 +36693,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "111599.01111111", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -36441,24 +36722,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "PIVXBTC" + "status": "BREAK", + "symbol": "CHATETH" }, { - "baseAsset": "PIVX", + "baseAsset": "STEEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -36469,14 +36750,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -36484,7 +36765,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26257.99565972", + "maxQty": "77415.40444753", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36511,24 +36792,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PIVXETH" + "symbol": "STEEMBTC" }, { - "baseAsset": "PIVX", + "baseAsset": "STEEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -36538,15 +36819,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -36554,7 +36835,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "118907.07612551", + "maxQty": "97402.17442668", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36581,24 +36862,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "PIVXBNB" + "status": "TRADING", + "symbol": "STEEMETH" }, { - "baseAsset": "IOST", + "baseAsset": "STEEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -36608,15 +36889,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -36624,7 +36905,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "194454701.00486111", + "maxQty": "27997.61868056", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36638,7 +36919,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -36649,27 +36930,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOSTBTC" + "status": "BREAK", + "symbol": "STEEMBNB" }, { - "baseAsset": "IOST", + "baseAsset": "NANO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -36680,14 +36960,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -36695,7 +36975,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1696568.27222222", + "maxQty": "14802.81705128", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36722,24 +37002,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOSTETH" + "status": "BREAK", + "symbol": "NANOBTC" }, { - "baseAsset": "CHAT", + "baseAsset": "NANO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -36750,19 +37030,25 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12886.93435897", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -36786,24 +37072,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "CHATBTC" + "symbol": "NANOETH" }, { - "baseAsset": "CHAT", + "baseAsset": "NANO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -36813,20 +37099,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "44269.81037344", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -36850,16 +37142,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "CHATETH" + "symbol": "NANOBNB" }, { - "baseAsset": "STEEM", + "baseAsset": "VIA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -36893,7 +37185,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "223818.19513889", + "maxQty": "82973.76406035", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36925,11 +37217,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "STEEMBTC" + "status": "BREAK", + "symbol": "VIABTC" }, { - "baseAsset": "STEEM", + "baseAsset": "VIA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -36955,7 +37247,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -36963,7 +37255,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "42188.29911806", + "maxQty": "113620.66435021", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -36995,11 +37287,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "STEEMETH" + "status": "BREAK", + "symbol": "VIAETH" }, { - "baseAsset": "STEEM", + "baseAsset": "VIA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37025,7 +37317,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -37033,7 +37325,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "27997.61868056", + "maxQty": "115272.51640408", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37066,18 +37358,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "STEEMBNB" + "symbol": "VIABNB" }, { - "baseAsset": "NANO", + "baseAsset": "BLZ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -37088,8 +37380,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -37103,7 +37395,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "67032.15123611", + "maxQty": "413595.29256428", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37117,7 +37409,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -37128,7 +37420,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -37136,18 +37429,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NANOBTC" + "symbol": "BLZBTC" }, { - "baseAsset": "NANO", + "baseAsset": "BLZ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -37158,14 +37451,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -37173,7 +37466,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8461.67467361", + "maxQty": "300963.74009728", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37206,18 +37499,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NANOETH" + "symbol": "BLZETH" }, { - "baseAsset": "NANO", + "baseAsset": "BLZ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -37228,14 +37521,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -37243,7 +37536,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "44269.81037344", + "maxQty": "187388.06666666", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37276,10 +37569,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "NANOBNB" + "symbol": "BLZBNB" }, { - "baseAsset": "VIA", + "baseAsset": "AE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37313,7 +37606,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "109344.42361111", + "maxQty": "146667.61099513", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37345,11 +37638,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "VIABTC" + "status": "BREAK", + "symbol": "AEBTC" }, { - "baseAsset": "VIA", + "baseAsset": "AE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37375,7 +37668,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -37383,7 +37676,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "113620.66435021", + "maxQty": "234526.18592078", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37416,10 +37709,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "VIAETH" + "symbol": "AEETH" }, { - "baseAsset": "VIA", + "baseAsset": "AE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37445,7 +37738,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -37453,7 +37746,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "115272.51640408", + "maxQty": "219076.24000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37486,10 +37779,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "VIABNB" + "symbol": "AEBNB" }, { - "baseAsset": "BLZ", + "baseAsset": "RPX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37521,12 +37814,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "430344.22500000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -37555,11 +37842,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BLZBTC" + "status": "BREAK", + "symbol": "RPXBTC" }, { - "baseAsset": "BLZ", + "baseAsset": "RPX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37585,18 +37872,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "146663.86666667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -37625,11 +37906,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BLZETH" + "status": "BREAK", + "symbol": "RPXETH" }, { - "baseAsset": "BLZ", + "baseAsset": "RPX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37655,18 +37936,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "108758.38888889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -37695,11 +37970,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BLZBNB" + "status": "BREAK", + "symbol": "RPXBNB" }, { - "baseAsset": "AE", + "baseAsset": "NCASH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37717,7 +37992,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -37733,7 +38008,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "146667.61099513", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37766,18 +38041,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "AEBTC" + "symbol": "NCASHBTC" }, { - "baseAsset": "AE", + "baseAsset": "NCASH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -37787,15 +38062,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -37803,7 +38078,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "234526.18592078", + "maxQty": "22032472.99027102", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37835,19 +38110,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "AEETH" + "status": "TRADING", + "symbol": "NCASHETH" }, { - "baseAsset": "AE", + "baseAsset": "NCASH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -37857,15 +38132,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -37873,7 +38148,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "219076.24000000", + "maxQty": "9222449.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -37906,10 +38181,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "AEBNB" + "symbol": "NCASHBNB" }, { - "baseAsset": "RPX", + "baseAsset": "POA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37941,6 +38216,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7693161.93324775", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -37970,10 +38251,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "RPXBTC" + "symbol": "POABTC" }, { - "baseAsset": "RPX", + "baseAsset": "POA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -37999,12 +38280,18 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1888168.90586630", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -38034,10 +38321,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "RPXETH" + "symbol": "POAETH" }, { - "baseAsset": "RPX", + "baseAsset": "POA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38063,12 +38350,18 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2311900.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -38098,10 +38391,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "RPXBNB" + "symbol": "POABNB" }, { - "baseAsset": "NCASH", + "baseAsset": "ZIL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38135,7 +38428,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1631219977.99175824", + "maxQty": "10738710.10632383", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38149,7 +38442,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -38160,18 +38453,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "NCASHBTC" + "status": "TRADING", + "symbol": "ZILBTC" }, { - "baseAsset": "NCASH", + "baseAsset": "ZIL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38197,7 +38491,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -38205,7 +38499,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "31816158.37361111", + "maxQty": "840610.34885337", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38238,10 +38532,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NCASHETH" + "symbol": "ZILETH" }, { - "baseAsset": "NCASH", + "baseAsset": "ZIL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38267,7 +38561,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -38275,7 +38569,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "21332000.00000000", + "maxQty": "653011.08964558", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38307,11 +38601,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "NCASHBNB" + "status": "TRADING", + "symbol": "ZILBNB" }, { - "baseAsset": "POA", + "baseAsset": "ONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38329,7 +38623,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -38345,7 +38639,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2596254.37430556", + "maxQty": "102582.59918693", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38359,7 +38653,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -38370,7 +38664,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -38378,18 +38673,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "POABTC" + "symbol": "ONTBTC" }, { - "baseAsset": "POA", + "baseAsset": "ONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -38407,7 +38702,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -38415,7 +38710,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1888168.90586630", + "maxQty": "45882.04308547", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38447,11 +38742,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "POAETH" + "status": "TRADING", + "symbol": "ONTETH" }, { - "baseAsset": "POA", + "baseAsset": "ONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38477,7 +38772,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -38485,7 +38780,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2311900.00000000", + "maxQty": "21987.54041666", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38518,10 +38813,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "POABNB" + "symbol": "ONTBNB" }, { - "baseAsset": "ZIL", + "baseAsset": "STORM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38539,7 +38834,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -38555,7 +38850,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12426368.41666667", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38569,7 +38864,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -38580,19 +38875,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZILBTC" + "status": "BREAK", + "symbol": "STORMBTC" }, { - "baseAsset": "ZIL", + "baseAsset": "STORM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -38618,7 +38912,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -38626,7 +38920,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1203846.36458333", + "maxQty": "25392055.54343294", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38658,17 +38952,17 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZILETH" + "status": "BREAK", + "symbol": "STORMETH" }, { - "baseAsset": "ZIL", + "baseAsset": "STORM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -38680,7 +38974,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -38688,7 +38982,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -38696,7 +38990,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "695558.48402778", + "maxQty": "9222449.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38728,19 +39022,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZILBNB" + "status": "BREAK", + "symbol": "STORMBNB" }, { - "baseAsset": "ONT", + "baseAsset": "QTUM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -38750,15 +39044,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -38766,7 +39060,77 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "420488.25350000", + "maxQty": "17651.31491713", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "QTUMBNB" + }, + { + "baseAsset": "QTUM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "30510.18928561", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38794,24 +39158,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONTBTC" + "symbol": "QTUMUSDT" }, { - "baseAsset": "ONT", + "baseAsset": "XEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -38822,14 +39186,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -38837,7 +39201,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "57810.36158333", + "maxQty": "1157510.27936066", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38864,24 +39228,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONTETH" + "symbol": "XEMBTC" }, { - "baseAsset": "ONT", + "baseAsset": "XEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -38891,15 +39255,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -38907,7 +39271,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16818.36180556", + "maxQty": "201426.83530229", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -38934,24 +39298,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONTBNB" + "symbol": "XEMETH" }, { - "baseAsset": "STORM", + "baseAsset": "XEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -38961,7 +39325,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -38969,7 +39333,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -38977,7 +39341,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "277389732.51772064", + "maxQty": "612988.04696133", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39004,16 +39368,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "STORMBTC" + "symbol": "XEMBNB" }, { - "baseAsset": "STORM", + "baseAsset": "WAN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39039,7 +39403,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -39047,7 +39411,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25392055.54343294", + "maxQty": "72483.04308547", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39074,16 +39438,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "STORMETH" + "status": "TRADING", + "symbol": "WANBTC" }, { - "baseAsset": "STORM", + "baseAsset": "WAN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39109,7 +39473,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -39117,7 +39481,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13403725.00000000", + "maxQty": "58710.47324530", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39144,24 +39508,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "STORMBNB" + "status": "TRADING", + "symbol": "WANETH" }, { - "baseAsset": "QTUM", + "baseAsset": "WAN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -39179,7 +39543,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -39187,7 +39551,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17651.31491713", + "maxQty": "20329.11064102", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39220,18 +39584,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "QTUMBNB" + "symbol": "WANBNB" }, { - "baseAsset": "QTUM", + "baseAsset": "WPR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -39241,15 +39605,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -39257,7 +39621,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "122724.51691597", + "maxQty": "14462894.19305555", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39271,7 +39635,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -39282,19 +39646,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "QTUMUSDT" + "status": "BREAK", + "symbol": "WPRBTC" }, { - "baseAsset": "XEM", + "baseAsset": "WPR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39320,7 +39683,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -39328,7 +39691,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1434165.23402778", + "maxQty": "3690618.07601573", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39355,16 +39718,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "XEMBTC" + "status": "BREAK", + "symbol": "WPRETH" }, { - "baseAsset": "XEM", + "baseAsset": "QLC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39390,7 +39753,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -39398,7 +39761,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "267081.03333333", + "maxQty": "1324770.90687977", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39425,24 +39788,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XEMETH" + "symbol": "QLCBTC" }, { - "baseAsset": "XEM", + "baseAsset": "QLC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -39452,7 +39815,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -39460,7 +39823,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -39468,7 +39831,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "612988.04696133", + "maxQty": "961537.75609756", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39495,16 +39858,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "XEMBNB" + "symbol": "QLCETH" }, { - "baseAsset": "WAN", + "baseAsset": "SYS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39538,7 +39901,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "186109.29652778", + "maxQty": "278844.91938846", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39571,18 +39934,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WANBTC" + "symbol": "SYSBTC" }, { - "baseAsset": "WAN", + "baseAsset": "SYS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -39593,14 +39956,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -39608,7 +39971,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "46135.13295139", + "maxQty": "991431.72651934", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39640,19 +40003,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "WANETH" + "status": "BREAK", + "symbol": "SYSETH" }, { - "baseAsset": "WAN", + "baseAsset": "SYS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -39663,14 +40026,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -39678,7 +40041,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "19941.12145833", + "maxQty": "453500.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39710,19 +40073,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "WANBNB" + "status": "BREAK", + "symbol": "SYSBNB" }, { - "baseAsset": "WPR", + "baseAsset": "QLC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -39732,7 +40095,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -39740,7 +40103,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -39748,7 +40111,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11488454.42222222", + "maxQty": "1132700.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39775,16 +40138,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "WPRBTC" + "status": "BREAK", + "symbol": "QLCBNB" }, { - "baseAsset": "WPR", + "baseAsset": "GRS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39810,7 +40173,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -39818,7 +40181,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3690618.07601573", + "maxQty": "97306.27380125", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39845,16 +40208,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "WPRETH" + "status": "TRADING", + "symbol": "GRSBTC" }, { - "baseAsset": "QLC", + "baseAsset": "GRS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -39880,7 +40243,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -39888,7 +40251,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2829189.58263889", + "maxQty": "170598.28532609", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39915,24 +40278,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "QLCBTC" + "status": "BREAK", + "symbol": "GRSETH" }, { - "baseAsset": "QLC", + "baseAsset": "ADA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -39942,15 +40305,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -39958,7 +40321,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "400593.39097222", + "maxQty": "3376463.39532314", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -39972,7 +40335,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -39983,26 +40346,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "QLCETH" + "symbol": "ADAUSDT" }, { - "baseAsset": "SYS", + "baseAsset": "ADA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -40012,15 +40376,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -40028,7 +40392,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "957377.76875000", + "maxQty": "193694.60826963", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40055,24 +40419,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SYSBTC" + "symbol": "ADABNB" }, { - "baseAsset": "SYS", + "baseAsset": "CLOAK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -40083,24 +40447,82 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "991431.72651934", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "CLOAKBTC" + }, + { + "baseAsset": "CLOAK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 }, { "filterType": "MAX_NUM_ORDERS", @@ -40131,18 +40553,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "SYSETH" + "symbol": "CLOAKETH" }, { - "baseAsset": "SYS", + "baseAsset": "GNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -40152,7 +40574,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -40160,7 +40582,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -40168,7 +40590,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "453500.00000000", + "maxQty": "319135.26197085", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40195,16 +40617,86 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "SYSBNB" + "symbol": "GNTBTC" }, { - "baseAsset": "QLC", + "baseAsset": "GNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "167396.23213046", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "GNTETH" + }, + { + "baseAsset": "GNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -40222,7 +40714,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "900000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -40238,7 +40730,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1132700.00000000", + "maxQty": "442900.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40271,10 +40763,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "QLCBNB" + "symbol": "GNTBNB" }, { - "baseAsset": "GRS", + "baseAsset": "LOOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -40308,7 +40800,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "75517.46805556", + "maxQty": "1274598.13829047", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40341,10 +40833,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GRSBTC" + "symbol": "LOOMBTC" }, { - "baseAsset": "GRS", + "baseAsset": "LOOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -40370,7 +40862,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -40378,7 +40870,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "170598.28532609", + "maxQty": "426680.77275886", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40410,19 +40902,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "GRSETH" + "status": "TRADING", + "symbol": "LOOMETH" }, { - "baseAsset": "ADA", + "baseAsset": "LOOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -40433,14 +40925,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -40448,7 +40940,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4235038.31930556", + "maxQty": "1353947.09556787", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40462,7 +40954,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -40473,27 +40965,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ADAUSDT" + "status": "BREAK", + "symbol": "LOOMBNB" }, { - "baseAsset": "ADA", + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -40503,7 +40994,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -40511,7 +41002,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -40519,7 +41010,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "292330.43361111", + "maxQty": "4755026.06532314", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40533,7 +41024,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -40544,26 +41035,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ADABNB" + "symbol": "XRPUSDT" }, { - "baseAsset": "CLOAK", + "baseAsset": "BCN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -40574,8 +41066,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -40616,18 +41108,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "CLOAKBTC" + "symbol": "BCNBTC" }, { - "baseAsset": "CLOAK", + "baseAsset": "BCN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -40638,14 +41130,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -40680,18 +41172,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "CLOAKETH" + "symbol": "BCNETH" }, { - "baseAsset": "GNT", + "baseAsset": "BCN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -40701,7 +41193,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -40709,18 +41201,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "319135.26197085", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -40744,24 +41230,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "GNTBTC" + "symbol": "BCNBNB" }, { - "baseAsset": "GNT", + "baseAsset": "REP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -40771,15 +41257,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -40787,7 +41273,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "167396.23213046", + "maxQty": "1782.07215427", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40801,7 +41287,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -40812,26 +41298,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "GNTETH" + "status": "TRADING", + "symbol": "REPBTC" }, { - "baseAsset": "GNT", + "baseAsset": "REP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -40842,14 +41329,14 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -40857,7 +41344,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "442900.00000000", + "maxQty": "2600.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40890,18 +41377,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "GNTBNB" + "symbol": "REPBNB" }, { - "baseAsset": "LOOM", + "baseAsset": "BTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -40911,15 +41398,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -40927,7 +41414,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1683786.94236111", + "maxQty": "3.25320229", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -40954,16 +41441,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LOOMBTC" + "symbol": "BTCTUSD" }, { - "baseAsset": "LOOM", + "baseAsset": "TUSD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -40989,18 +41476,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "527327.40763889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41024,24 +41505,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LOOMETH" + "status": "BREAK", + "symbol": "TUSDBTC" }, { - "baseAsset": "LOOM", + "baseAsset": "ETH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -41051,15 +41532,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -41067,7 +41548,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1353947.09556787", + "maxQty": "35.64985359", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41094,16 +41575,80 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHTUSD" + }, + { + "baseAsset": "TUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "LOOMBNB" + "symbol": "TUSDETH" }, { - "baseAsset": "XRP", + "baseAsset": "TUSD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -41129,18 +41674,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "9644858.35138889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41151,7 +41690,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -41162,27 +41701,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "XRPUSDT" + "status": "BREAK", + "symbol": "TUSDBNB" }, { - "baseAsset": "BCN", + "baseAsset": "ZEN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -41193,8 +41731,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -41206,6 +41744,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "955.95225156", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41234,19 +41778,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCNBTC" + "status": "TRADING", + "symbol": "ZENBTC" }, { - "baseAsset": "BCN", + "baseAsset": "ZEN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -41256,20 +41800,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "628.57701181", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41298,19 +41848,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCNETH" + "status": "TRADING", + "symbol": "ZENETH" }, { - "baseAsset": "BCN", + "baseAsset": "ZEN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -41320,20 +41870,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "925.36080611", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41362,19 +41918,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCNBNB" + "status": "TRADING", + "symbol": "ZENBNB" }, { - "baseAsset": "REP", + "baseAsset": "SKY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -41384,9 +41940,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -41400,7 +41956,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2550.45035694", + "maxQty": "86318.75224646", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41414,7 +41970,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -41425,19 +41981,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "REPBTC" + "status": "BREAK", + "symbol": "SKYBTC" }, { - "baseAsset": "REP", + "baseAsset": "SKY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -41463,7 +42018,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -41471,7 +42026,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "439.93165069", + "maxQty": "57402.96554357", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41503,19 +42058,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "REPETH" + "status": "BREAK", + "symbol": "SKYETH" }, { - "baseAsset": "REP", + "baseAsset": "SKY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -41525,15 +42080,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -41541,7 +42096,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2600.00000000", + "maxQty": "48002.55513499", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41574,18 +42129,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "REPBNB" + "symbol": "SKYBNB" }, { - "baseAsset": "BTC", + "baseAsset": "EOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -41595,9 +42150,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -41611,7 +42166,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10.33541518", + "maxQty": "225292.88436414", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41625,7 +42180,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -41636,26 +42191,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCTUSD" + "symbol": "EOSUSDT" }, { - "baseAsset": "TUSD", + "baseAsset": "EOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -41665,20 +42221,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "25755.45677553", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41702,24 +42264,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "TUSDBTC" + "status": "TRADING", + "symbol": "EOSBNB" }, { - "baseAsset": "ETH", + "baseAsset": "CVC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -41729,15 +42291,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -41745,7 +42307,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "178.26573808", + "maxQty": "163271.13134120", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41772,24 +42334,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHTUSD" + "symbol": "CVCBTC" }, { - "baseAsset": "TUSD", + "baseAsset": "CVC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -41807,12 +42369,18 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "114733.89159138", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41841,19 +42409,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "TUSDETH" + "status": "TRADING", + "symbol": "CVCETH" }, { - "baseAsset": "TUSD", + "baseAsset": "CVC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -41864,19 +42432,25 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "343400.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -41906,10 +42480,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TUSDBNB" + "symbol": "CVCBNB" }, { - "baseAsset": "ZEN", + "baseAsset": "THETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -41928,8 +42502,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -41943,7 +42517,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3665.64313194", + "maxQty": "93254.10416956", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -41957,7 +42531,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -41968,7 +42542,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -41976,18 +42551,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZENBTC" + "symbol": "THETABTC" }, { - "baseAsset": "ZEN", + "baseAsset": "THETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -41997,15 +42572,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -42013,7 +42588,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1374.26294236", + "maxQty": "18340.35288394", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42046,18 +42621,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZENETH" + "symbol": "THETAETH" }, { - "baseAsset": "ZEN", + "baseAsset": "THETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -42067,15 +42642,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -42083,7 +42658,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "560.70736111", + "maxQty": "25499.97241139", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42116,18 +42691,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZENBNB" + "symbol": "THETABNB" }, { - "baseAsset": "SKY", + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -42137,7 +42712,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -42145,7 +42720,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -42153,7 +42728,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "65658.59027778", + "maxQty": "207877.75469075", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42180,112 +42755,42 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SKYBTC" - }, - { - "baseAsset": "SKY", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "57402.96554357", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "ETH", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "SKYETH" + "symbol": "XRPBNB" }, { - "baseAsset": "SKY", + "baseAsset": "TUSD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "1.29980000", + "minPrice": "0.69990000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -42293,7 +42798,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48002.55513499", + "maxQty": "2769040.04617095", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42320,16 +42825,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "SKYBNB" + "status": "TRADING", + "symbol": "TUSDUSDT" }, { - "baseAsset": "EOS", + "baseAsset": "IOTA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -42348,8 +42853,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -42363,7 +42868,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "441295.66308333", + "maxQty": "404012.59849895", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42397,16 +42902,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EOSUSDT" + "symbol": "IOTAUSDT" }, { - "baseAsset": "EOS", + "baseAsset": "XLM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00010000", "tickSize": "0.00010000" }, @@ -42418,15 +42923,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -42434,7 +42939,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17285.10795833", + "maxQty": "2608597.23287004", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42448,7 +42953,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -42459,18 +42964,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EOSBNB" + "symbol": "XLMUSDT" }, { - "baseAsset": "CVC", + "baseAsset": "IOTX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -42504,7 +43010,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "503352.74583333", + "maxQty": "3317262.68102849", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42518,7 +43024,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -42529,7 +43035,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -42537,10 +43044,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CVCBTC" + "symbol": "IOTXBTC" }, { - "baseAsset": "CVC", + "baseAsset": "IOTX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -42566,7 +43073,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -42574,7 +43081,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "82892.63819444", + "maxQty": "711433.31966643", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42607,18 +43114,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CVCETH" + "symbol": "IOTXETH" }, { - "baseAsset": "CVC", + "baseAsset": "QKC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -42628,7 +43135,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -42636,7 +43143,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -42644,7 +43151,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "343400.00000000", + "maxQty": "13487077.17512161", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42671,16 +43178,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "CVCBNB" + "status": "TRADING", + "symbol": "QKCBTC" }, { - "baseAsset": "THETA", + "baseAsset": "QKC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -42706,7 +43213,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -42714,7 +43221,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "246753.13333333", + "maxQty": "1759147.79499652", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42728,7 +43235,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -42739,19 +43246,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "THETABTC" + "symbol": "QKCETH" }, { - "baseAsset": "THETA", + "baseAsset": "AGI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -42777,7 +43283,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -42785,7 +43291,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26757.86180556", + "maxQty": "745675.59871794", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42812,24 +43318,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "THETAETH" + "status": "BREAK", + "symbol": "AGIBTC" }, { - "baseAsset": "THETA", + "baseAsset": "AGI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -42839,7 +43345,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -42847,7 +43353,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -42855,7 +43361,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11996.80000000", + "maxQty": "3511231.29076087", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42882,24 +43388,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "THETABNB" + "status": "BREAK", + "symbol": "AGIETH" }, { - "baseAsset": "XRP", + "baseAsset": "AGI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -42910,14 +43416,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -42925,7 +43431,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "209684.51270833", + "maxQty": "1185463.06916780", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -42957,19 +43463,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "XRPBNB" + "status": "BREAK", + "symbol": "AGIBNB" }, { - "baseAsset": "TUSD", + "baseAsset": "NXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -42979,15 +43485,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -42995,7 +43501,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "257952.33365278", + "maxQty": "66386.16122307", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43022,24 +43528,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TUSDUSDT" + "symbol": "NXSBTC" }, { - "baseAsset": "IOTA", + "baseAsset": "NXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -43049,7 +43555,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "90000000.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -43057,7 +43563,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -43065,7 +43571,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "869917.69534028", + "maxQty": "120324.84165746", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43079,7 +43585,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -43090,19 +43596,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOTAUSDT" + "status": "BREAK", + "symbol": "NXSETH" }, { - "baseAsset": "XLM", + "baseAsset": "NXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -43128,149 +43633,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "6445195.12354167", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "XLMUSDT" - }, - { - "baseAsset": "IOTX", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "52989248.09444444", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOTXBTC" - }, - { - "baseAsset": "IOTX", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -43278,7 +43641,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1128292.97708333", + "maxQty": "145584.02703412", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43305,24 +43668,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOTXETH" + "status": "BREAK", + "symbol": "NXSBNB" }, { - "baseAsset": "QKC", + "baseAsset": "ENJ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -43332,15 +43695,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -43348,7 +43711,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "91223293.48958333", + "maxQty": "22133.14120917", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43375,16 +43738,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "QKCBTC" + "symbol": "ENJBNB" }, { - "baseAsset": "QKC", + "baseAsset": "DATA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -43410,7 +43773,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -43418,7 +43781,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1371162.95138889", + "maxQty": "1191058.61779013", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43445,16 +43808,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "QKCETH" + "symbol": "DATABTC" }, { - "baseAsset": "AGI", + "baseAsset": "DATA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -43480,7 +43843,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -43488,7 +43851,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1088864.22916667", + "maxQty": "482047.55107713", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43515,24 +43878,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AGIBTC" + "symbol": "DATAETH" }, { - "baseAsset": "AGI", + "baseAsset": "ONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -43542,7 +43905,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "900000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -43550,7 +43913,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -43558,7 +43921,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3511231.29076087", + "maxQty": "233293.02710215", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43572,7 +43935,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -43583,26 +43946,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "AGIETH" + "status": "TRADING", + "symbol": "ONTUSDT" }, { - "baseAsset": "AGI", + "baseAsset": "TRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -43620,7 +43984,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -43628,7 +43992,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1185463.06916780", + "maxQty": "1817876.35232800", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43660,19 +44024,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "AGIBNB" + "status": "TRADING", + "symbol": "TRXBNB" }, { - "baseAsset": "NXS", + "baseAsset": "TRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -43682,15 +44046,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -43698,7 +44062,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "72157.02500000", + "maxQty": "15641396.94961779", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43712,7 +44076,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -43723,26 +44087,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NXSBTC" + "symbol": "TRXUSDT" }, { - "baseAsset": "NXS", + "baseAsset": "ETC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -43752,7 +44117,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "90000.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -43760,7 +44125,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -43768,7 +44133,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "120324.84165746", + "maxQty": "20286.26875608", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43782,7 +44147,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -43793,26 +44158,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "NXSETH" + "status": "TRADING", + "symbol": "ETCUSDT" }, { - "baseAsset": "NXS", + "baseAsset": "ETC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -43823,14 +44189,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -43838,7 +44204,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "145584.02703412", + "maxQty": "1186.43999305", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43870,19 +44236,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "NXSBNB" + "status": "TRADING", + "symbol": "ETCBNB" }, { - "baseAsset": "ENJ", + "baseAsset": "ICX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -43892,15 +44258,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -43908,7 +44274,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "56968.71805556", + "maxQty": "229299.27162612", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43922,7 +44288,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -43933,18 +44299,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ENJBNB" + "symbol": "ICXUSDT" }, { - "baseAsset": "DATA", + "baseAsset": "SC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -43962,7 +44329,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -43978,7 +44345,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "948982.65555556", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -43992,7 +44359,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -44003,7 +44370,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -44011,10 +44379,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DATABTC" + "symbol": "SCBTC" }, { - "baseAsset": "DATA", + "baseAsset": "SC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -44040,7 +44408,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -44048,7 +44416,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "209186.84305556", + "maxQty": "2333634.71785962", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44081,89 +44449,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DATAETH" - }, - { - "baseAsset": "ONT", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "626869.55468750", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "ONTUSDT" + "symbol": "SCETH" }, { - "baseAsset": "TRX", + "baseAsset": "NPXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -44173,7 +44470,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -44181,7 +44478,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -44189,7 +44486,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2005650.60138889", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44216,24 +44513,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "TRXBNB" + "status": "BREAK", + "symbol": "NPXSBTC" }, { - "baseAsset": "TRX", + "baseAsset": "NPXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -44243,15 +44540,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -44259,7 +44556,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "54171374.32166667", + "maxQty": "42381097.76372481", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44273,7 +44570,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -44284,19 +44581,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "TRXUSDT" + "status": "BREAK", + "symbol": "NPXSETH" }, { - "baseAsset": "ETC", + "baseAsset": "VEN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -44328,12 +44624,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "141189.80277778", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -44344,7 +44634,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -44355,27 +44645,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ETCUSDT" + "status": "BREAK", + "symbol": "VENUSDT" }, { - "baseAsset": "ETC", + "baseAsset": "KEY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -44385,15 +44674,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -44401,7 +44690,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1695.66090278", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44428,24 +44717,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ETCBNB" + "status": "BREAK", + "symbol": "KEYBTC" }, { - "baseAsset": "ICX", + "baseAsset": "KEY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -44455,15 +44744,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -44471,7 +44760,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "385019.13295833", + "maxQty": "8724388.58304378", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44498,24 +44787,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ICXUSDT" + "symbol": "KEYETH" }, { - "baseAsset": "SC", + "baseAsset": "NAS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -44541,7 +44830,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "524725952.67152778", + "maxQty": "586241.68423905", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44555,7 +44844,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -44566,8 +44855,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -44575,18 +44863,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SCBTC" + "symbol": "NASBTC" }, { - "baseAsset": "SC", + "baseAsset": "NAS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -44596,7 +44884,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -44604,7 +44892,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -44612,7 +44900,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3684016.93958333", + "maxQty": "61811.04100069", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44645,18 +44933,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SCETH" + "symbol": "NASETH" }, { - "baseAsset": "SC", + "baseAsset": "NAS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -44667,14 +44955,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -44682,7 +44970,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3043210.71180556", + "maxQty": "48429.59247606", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44714,11 +45002,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "SCBNB" + "status": "BREAK", + "symbol": "NASBNB" }, { - "baseAsset": "NPXS", + "baseAsset": "MFT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -44736,7 +45024,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -44752,7 +45040,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4340000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44785,10 +45073,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "NPXSBTC" + "symbol": "MFTBTC" }, { - "baseAsset": "NPXS", + "baseAsset": "MFT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -44814,7 +45102,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -44822,7 +45110,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "285855145.00555556", + "maxQty": "3762251.95482974", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44855,18 +45143,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NPXSETH" + "symbol": "MFTETH" }, { - "baseAsset": "VEN", + "baseAsset": "MFT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -44876,20 +45164,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1630962.53472222", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -44913,16 +45207,157 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "VENUSDT" + "symbol": "MFTBNB" }, { - "baseAsset": "KEY", + "baseAsset": "DENT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DENTBTC" + }, + { + "baseAsset": "DENT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "26938421.15635858", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DENTETH" + }, + { + "baseAsset": "ARDR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -44956,7 +45391,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "295558799.54258242", + "maxQty": "230370.17512161", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -44988,11 +45423,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "KEYBTC" + "status": "TRADING", + "symbol": "ARDRBTC" }, { - "baseAsset": "KEY", + "baseAsset": "ARDR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45018,7 +45453,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -45026,7 +45461,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5493058.43333333", + "maxQty": "414041.47885402", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45058,81 +45493,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "KEYETH" - }, - { - "baseAsset": "NAS", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "126243.86447222", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "NASBTC" + "status": "BREAK", + "symbol": "ARDRETH" }, { - "baseAsset": "NAS", + "baseAsset": "ARDR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45150,15 +45515,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -45166,7 +45531,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "27946.46823611", + "maxQty": "331400.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45193,24 +45558,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "NASETH" + "status": "BREAK", + "symbol": "ARDRBNB" }, { - "baseAsset": "NAS", + "baseAsset": "NULS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -45220,15 +45585,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -45236,7 +45601,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48429.59247606", + "maxQty": "252680.95252953", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45250,7 +45615,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -45261,18 +45626,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "NASBNB" + "status": "TRADING", + "symbol": "NULSUSDT" }, { - "baseAsset": "MFT", + "baseAsset": "HOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45290,7 +45656,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -45306,7 +45672,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "587455554.16758242", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45339,10 +45705,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "MFTBTC" + "symbol": "HOTBTC" }, { - "baseAsset": "MFT", + "baseAsset": "HOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45368,7 +45734,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -45376,7 +45742,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4821853.85972222", + "maxQty": "16962619.01876302", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45409,18 +45775,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MFTETH" + "symbol": "HOTETH" }, { - "baseAsset": "MFT", + "baseAsset": "VET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -45430,7 +45796,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -45438,7 +45804,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -45446,7 +45812,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3561858.35833333", + "maxQty": "20422051.81584433", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45460,7 +45826,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -45471,18 +45837,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MFTBNB" + "symbol": "VETBTC" }, { - "baseAsset": "DENT", + "baseAsset": "VET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45508,7 +45875,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -45516,7 +45883,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1830000000.00000000", + "maxQty": "2248293.43780403", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45543,24 +45910,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DENTBTC" + "status": "TRADING", + "symbol": "VETETH" }, { - "baseAsset": "DENT", + "baseAsset": "VET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -45570,15 +45937,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -45586,7 +45953,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "405710622.88263889", + "maxQty": "22865275.40625433", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45600,7 +45967,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -45611,26 +45978,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DENTETH" + "symbol": "VETUSDT" }, { - "baseAsset": "ARDR", + "baseAsset": "VET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -45640,7 +46008,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -45648,7 +46016,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -45656,7 +46024,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "499525.33750000", + "maxQty": "1608141.16539263", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45683,16 +46051,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ARDRBTC" + "symbol": "VETBNB" }, { - "baseAsset": "ARDR", + "baseAsset": "DOCK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45718,7 +46086,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -45726,7 +46094,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "414041.47885402", + "maxQty": "1339741.39819318", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45740,7 +46108,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -45751,26 +46119,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ARDRETH" + "status": "TRADING", + "symbol": "DOCKBTC" }, { - "baseAsset": "ARDR", + "baseAsset": "DOCK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -45780,7 +46149,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -45788,7 +46157,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -45796,7 +46165,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "331400.00000000", + "maxQty": "4489461.95555556", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45823,24 +46192,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ARDRBNB" + "symbol": "DOCKETH" }, { - "baseAsset": "NULS", + "baseAsset": "POLY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -45850,15 +46219,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -45866,7 +46235,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "145353.67017361", + "maxQty": "124485.58790826", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45893,24 +46262,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NULSUSDT" + "symbol": "POLYBTC" }, { - "baseAsset": "HOT", + "baseAsset": "POLY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -45920,7 +46289,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -45928,7 +46297,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -45936,7 +46305,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8381508654.98194444", + "maxQty": "901676.36263736", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -45963,16 +46332,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "HOTBTC" + "symbol": "POLYBNB" }, { - "baseAsset": "HOT", + "baseAsset": "PHX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -45998,7 +46367,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -46006,7 +46375,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "189866829.75138889", + "maxQty": "79612100.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46033,87 +46402,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "HOTETH" - }, - { - "baseAsset": "VET", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "146664836.09930556", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "VETBTC" + "status": "BREAK", + "symbol": "PHXBTC" }, { - "baseAsset": "VET", + "baseAsset": "PHX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -46139,7 +46437,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -46147,7 +46445,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8088914.00347222", + "maxQty": "7740200.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46179,11 +46477,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "VETETH" + "status": "BREAK", + "symbol": "PHXETH" }, { - "baseAsset": "VET", + "baseAsset": "PHX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -46209,7 +46507,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -46217,78 +46515,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "30331127.80138889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "VETUSDT" - }, - { - "baseAsset": "VET", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1673408.64097222", + "maxQty": "3980900.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46320,19 +46547,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "VETBNB" + "status": "BREAK", + "symbol": "PHXBNB" }, { - "baseAsset": "DOCK", + "baseAsset": "HC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -46343,8 +46570,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -46358,7 +46585,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8330538.18958333", + "maxQty": "6433.36132128", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46372,7 +46599,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -46383,27 +46610,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "DOCKBTC" + "status": "BREAK", + "symbol": "HCBTC" }, { - "baseAsset": "DOCK", + "baseAsset": "HC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -46414,14 +46640,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -46429,7 +46655,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4489461.95555556", + "maxQty": "52419.49426593", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46462,10 +46688,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DOCKETH" + "symbol": "HCETH" }, { - "baseAsset": "POLY", + "baseAsset": "GO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -46499,7 +46725,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "918518.37777778", + "maxQty": "4152394.65948575", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46532,18 +46758,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "POLYBTC" + "symbol": "GOBTC" }, { - "baseAsset": "POLY", + "baseAsset": "GO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -46561,7 +46787,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -46569,7 +46795,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "901676.36263736", + "maxQty": "1378901.05471956", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46602,10 +46828,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "POLYBNB" + "symbol": "GOBNB" }, { - "baseAsset": "PHX", + "baseAsset": "PAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -46637,12 +46863,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "79612100.00000000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -46672,18 +46892,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PHXBTC" + "symbol": "PAXBTC" }, { - "baseAsset": "PHX", + "baseAsset": "PAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -46693,26 +46913,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "7740200.00000000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -46736,42 +46950,42 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PHXETH" + "symbol": "PAXBNB" }, { - "baseAsset": "PHX", + "baseAsset": "PAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "1.30000000", + "minPrice": "0.70000000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -46779,7 +46993,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3980900.00000000", + "maxQty": "1328601.35777920", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46806,24 +47020,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PHXBNB" + "symbol": "PAXUSDT" }, { - "baseAsset": "HC", + "baseAsset": "PAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -46834,25 +47048,19 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "6433.36132128", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -46876,24 +47084,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "HCBTC" + "symbol": "PAXETH" }, { - "baseAsset": "HC", + "baseAsset": "RVN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -46904,14 +47112,14 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -46919,7 +47127,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "52419.49426593", + "maxQty": "4629901.38220986", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -46933,7 +47141,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -46944,26 +47152,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "HCETH" + "status": "TRADING", + "symbol": "RVNBTC" }, { - "baseAsset": "GO", + "baseAsset": "DCR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -46973,9 +47182,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -46989,7 +47198,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20643286.96597222", + "maxQty": "485.75092425", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47022,18 +47231,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GOBTC" + "symbol": "DCRBTC" }, { - "baseAsset": "GO", + "baseAsset": "DCR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -47043,15 +47252,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -47059,7 +47268,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1378901.05471956", + "maxQty": "6753.08374352", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47092,10 +47301,74 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "GOBNB" + "symbol": "DCRBNB" }, { - "baseAsset": "PAX", + "baseAsset": "USDC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "USDCBNB" + }, + { + "baseAsset": "MITH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -47127,6 +47400,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2349146.34398888", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -47137,7 +47416,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -47148,26 +47427,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "PAXBTC" + "status": "TRADING", + "symbol": "MITHBTC" }, { - "baseAsset": "PAX", + "baseAsset": "MITH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -47178,19 +47458,25 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1105468.31827658", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -47219,19 +47505,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "PAXBNB" + "status": "TRADING", + "symbol": "MITHBNB" }, { - "baseAsset": "PAX", + "baseAsset": "BCHABC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -47241,15 +47527,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -47257,7 +47543,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "180521.65290278", + "maxQty": "31000.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47284,24 +47570,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "PAXUSDT" + "status": "BREAK", + "symbol": "BCHABCBTC" }, { - "baseAsset": "PAX", + "baseAsset": "BCHSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -47311,15 +47597,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -47348,24 +47634,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PAXETH" + "symbol": "BCHSVBTC" }, { - "baseAsset": "RVN", + "baseAsset": "BCHABC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -47375,15 +47661,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -47391,7 +47677,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20354047.56250000", + "maxQty": "22000.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47405,7 +47691,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -47416,27 +47702,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "RVNBTC" + "status": "BREAK", + "symbol": "BCHABCUSDT" }, { - "baseAsset": "RVN", + "baseAsset": "BCHSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -47446,26 +47731,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "841093.14305556", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -47489,24 +47768,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "RVNBNB" + "status": "BREAK", + "symbol": "BCHSVUSDT" }, { - "baseAsset": "DCR", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -47516,7 +47795,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -47524,7 +47803,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -47532,7 +47811,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2265.03360972", + "maxQty": "46.95709948", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47559,24 +47838,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "DCRBTC" + "status": "BREAK", + "symbol": "BNBPAX" }, { - "baseAsset": "DCR", + "baseAsset": "BTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -47586,15 +47865,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -47602,7 +47881,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6753.08374352", + "maxQty": "4.92222608", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47629,24 +47908,94 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DCRBNB" + "symbol": "BTCPAX" }, { - "baseAsset": "USDC", + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "56.11078035", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "PAX", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ETHPAX" + }, + { + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -47656,20 +48005,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "30610.99623611", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -47693,24 +48048,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "USDCBNB" + "symbol": "XRPPAX" }, { - "baseAsset": "MITH", + "baseAsset": "EOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -47720,15 +48075,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -47736,7 +48091,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20180596.01736111", + "maxQty": "36809.16131649", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47750,7 +48105,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -47761,27 +48116,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "MITHBTC" + "status": "BREAK", + "symbol": "EOSPAX" }, { - "baseAsset": "MITH", + "baseAsset": "XLM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -47792,14 +48146,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -47807,7 +48161,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "746887.35555556", + "maxQty": "644317.50078637", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47834,24 +48188,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "MITHBNB" + "status": "BREAK", + "symbol": "XLMPAX" }, { - "baseAsset": "BCHABC", + "baseAsset": "REN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -47861,9 +48215,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -47877,7 +48231,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "31000.00000000", + "maxQty": "179530.97567755", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -47891,7 +48245,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -47902,18 +48256,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCHABCBTC" + "status": "TRADING", + "symbol": "RENBTC" }, { - "baseAsset": "BCHSV", + "baseAsset": "REN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -47931,20 +48286,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "442309.85734072", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -47968,24 +48329,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BCHSVBTC" + "symbol": "RENBNB" }, { - "baseAsset": "BCHABC", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -47995,9 +48356,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -48011,7 +48372,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "22000.00000000", + "maxQty": "46.01125031", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48038,24 +48399,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCHABCUSDT" + "status": "TRADING", + "symbol": "BNBTUSD" }, { - "baseAsset": "BCHSV", + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -48065,9 +48426,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -48079,6 +48440,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "63788.52953439", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -48102,16 +48469,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCHSVUSDT" + "status": "TRADING", + "symbol": "XRPTUSD" }, { - "baseAsset": "BNB", + "baseAsset": "EOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -48145,7 +48512,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "193.52045833", + "maxQty": "3630.31080872", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48172,24 +48539,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BNBPAX" + "status": "BREAK", + "symbol": "EOSTUSD" }, { - "baseAsset": "BTC", + "baseAsset": "XLM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -48199,9 +48566,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -48215,7 +48582,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23.99394775", + "maxQty": "314375.13858921", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48242,24 +48609,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BTCPAX" + "status": "BREAK", + "symbol": "XLMTUSD" }, { - "baseAsset": "ETH", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -48269,9 +48636,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -48285,7 +48652,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "200.63959833", + "maxQty": "265.56725642", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48312,24 +48679,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHPAX" + "symbol": "BNBUSDC" }, { - "baseAsset": "XRP", + "baseAsset": "BTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -48339,9 +48706,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -48355,7 +48722,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17416.91145833", + "maxQty": "65.11201125", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48369,7 +48736,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -48380,26 +48747,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPPAX" + "symbol": "BTCUSDC" }, { - "baseAsset": "EOS", + "baseAsset": "ETH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -48409,9 +48777,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -48425,7 +48793,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "36809.16131649", + "maxQty": "564.64261271", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48439,7 +48807,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -48450,26 +48818,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "EOSPAX" + "status": "TRADING", + "symbol": "ETHUSDC" }, { - "baseAsset": "XLM", + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -48479,9 +48848,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -48495,7 +48864,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "644317.50078637", + "maxQty": "225376.79360667", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48509,7 +48878,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -48520,26 +48889,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "XLMPAX" + "status": "TRADING", + "symbol": "XRPUSDC" }, { - "baseAsset": "REN", + "baseAsset": "EOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -48549,15 +48919,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -48565,7 +48935,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "362856.83958333", + "maxQty": "33547.78872828", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48593,24 +48963,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RENBTC" + "symbol": "EOSUSDC" }, { - "baseAsset": "REN", + "baseAsset": "XLM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -48621,14 +48991,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -48636,7 +49006,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "442309.85734072", + "maxQty": "1401500.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48663,25 +49033,96 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "RENBNB" + "symbol": "XLMUSDC" }, { - "baseAsset": "BNB", + "baseAsset": "USDC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", + "maxPrice": "1.30000000", + "minPrice": "0.70000000", "tickSize": "0.00010000" }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "USDCUSDT" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", @@ -48691,8 +49132,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -48706,7 +49147,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "431.04740972", + "maxQty": "53188.71142460", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48739,10 +49180,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBTUSD" + "symbol": "ADATUSD" }, { - "baseAsset": "XRP", + "baseAsset": "TRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -48776,7 +49217,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "120585.18000000", + "maxQty": "515419.59972202", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48809,18 +49250,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPTUSD" + "symbol": "TRXTUSD" }, { - "baseAsset": "EOS", + "baseAsset": "NEO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -48830,9 +49271,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -48846,7 +49287,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2530.33460417", + "maxQty": "2330.53262857", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -48878,11 +49319,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "EOSTUSD" + "status": "BREAK", + "symbol": "NEOTUSD" }, { - "baseAsset": "XLM", + "baseAsset": "TRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -48916,77 +49357,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "314375.13858921", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "TUSD", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "XLMTUSD" - }, - { - "baseAsset": "BNB", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "2823.76049306", + "maxQty": "2287207.59478804", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49013,95 +49384,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "BNBUSDC" - }, - { - "baseAsset": "BTC", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "178.38628782", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDC", + "quoteAsset": "XRP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCUSDC" + "symbol": "TRXXRP" }, { - "baseAsset": "ETH", + "baseAsset": "XZC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -49112,8 +49412,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -49127,7 +49427,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1127.26352046", + "maxQty": "8961.03207094", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49141,7 +49441,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -49152,27 +49452,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "XRP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ETHUSDC" + "status": "BREAK", + "symbol": "XZCXRP" }, { - "baseAsset": "XRP", + "baseAsset": "PAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -49182,9 +49481,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49198,7 +49497,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "661735.02902778", + "maxQty": "39124.69718750", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49225,16 +49524,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "XRPUSDC" + "status": "BREAK", + "symbol": "PAXTUSD" }, { - "baseAsset": "EOS", + "baseAsset": "USDC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -49268,7 +49567,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "38504.61329167", + "maxQty": "43417.20380556", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49295,24 +49594,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "EOSUSDC" + "status": "BREAK", + "symbol": "USDCTUSD" }, { - "baseAsset": "XLM", + "baseAsset": "USDC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -49322,9 +49621,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49338,7 +49637,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1401500.00000000", + "maxQty": "50031.87694149", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49365,24 +49664,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "XLMUSDC" + "symbol": "USDCPAX" }, { - "baseAsset": "USDC", + "baseAsset": "LINK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -49392,7 +49691,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "90000.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -49408,7 +49707,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3720705.78597917", + "maxQty": "140595.03364767", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49442,18 +49741,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDCUSDT" + "symbol": "LINKUSDT" }, { - "baseAsset": "ADA", + "baseAsset": "LINK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -49463,9 +49762,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49479,7 +49778,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "155228.39562500", + "maxQty": "942.02511111", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49511,19 +49810,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ADATUSD" + "status": "BREAK", + "symbol": "LINKTUSD" }, { - "baseAsset": "TRX", + "baseAsset": "LINK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -49533,9 +49832,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49549,7 +49848,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "573925.84354167", + "maxQty": "9793.80510373", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49576,24 +49875,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "TRXTUSD" + "status": "BREAK", + "symbol": "LINKPAX" }, { - "baseAsset": "NEO", + "baseAsset": "LINK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -49604,8 +49903,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49619,7 +49918,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2330.53262857", + "maxQty": "16486.80951285", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49646,24 +49945,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "NEOTUSD" + "status": "TRADING", + "symbol": "LINKUSDC" }, { - "baseAsset": "TRX", + "baseAsset": "WAVES", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -49673,9 +49972,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49689,7 +49988,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1485167.54243056", + "maxQty": "25438.95085476", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49703,7 +50002,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -49714,26 +50013,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "XRP", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRXXRP" + "symbol": "WAVESUSDT" }, { - "baseAsset": "XZC", + "baseAsset": "WAVES", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -49743,9 +50043,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -49759,7 +50059,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8961.03207094", + "maxQty": "14907.41663435", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49786,16 +50086,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "XRP", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "XZCXRP" + "symbol": "WAVESTUSD" }, { - "baseAsset": "PAX", + "baseAsset": "WAVES", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -49829,7 +50129,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "39124.69718750", + "maxQty": "8500.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49856,16 +50156,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PAXTUSD" + "symbol": "WAVESPAX" }, { - "baseAsset": "USDC", + "baseAsset": "WAVES", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -49899,7 +50199,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43417.20380556", + "maxQty": "18619.36144947", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49926,24 +50226,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "USDCTUSD" + "symbol": "WAVESUSDC" }, { - "baseAsset": "USDC", + "baseAsset": "BCHABC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -49953,9 +50253,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -49969,7 +50269,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50031.87694149", + "maxQty": "1100.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -49996,24 +50296,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "USDCPAX" + "symbol": "BCHABCTUSD" }, { - "baseAsset": "LINK", + "baseAsset": "BCHABC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -50023,9 +50323,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -50039,7 +50339,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "95234.81577778", + "maxQty": "600.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50053,7 +50353,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -50064,27 +50364,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LINKUSDT" + "status": "BREAK", + "symbol": "BCHABCPAX" }, { - "baseAsset": "LINK", + "baseAsset": "BCHABC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -50094,9 +50393,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -50110,7 +50409,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3189.31495139", + "maxQty": "1700.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50137,24 +50436,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LINKTUSD" + "status": "BREAK", + "symbol": "BCHABCUSDC" }, { - "baseAsset": "LINK", + "baseAsset": "BCHSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -50164,9 +50463,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -50178,12 +50477,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "9793.80510373", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -50207,24 +50500,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "LINKPAX" + "symbol": "BCHSVTUSD" }, { - "baseAsset": "LINK", + "baseAsset": "BCHSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -50234,9 +50527,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -50248,12 +50541,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "7703.41866667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -50277,24 +50564,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LINKUSDC" + "status": "BREAK", + "symbol": "BCHSVPAX" }, { - "baseAsset": "WAVES", + "baseAsset": "BCHSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -50304,9 +50591,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -50318,12 +50605,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "45394.67753472", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -50334,7 +50615,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -50345,27 +50626,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "WAVESUSDT" + "status": "BREAK", + "symbol": "BCHSVUSDC" }, { - "baseAsset": "WAVES", + "baseAsset": "LTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -50375,9 +50655,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -50391,7 +50671,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "14907.41663435", + "maxQty": "152.25276794", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50424,18 +50704,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "WAVESTUSD" + "symbol": "LTCTUSD" }, { - "baseAsset": "WAVES", + "baseAsset": "LTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -50445,9 +50725,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -50461,7 +50741,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8500.00000000", + "maxQty": "101.58068045", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50494,18 +50774,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "WAVESPAX" + "symbol": "LTCPAX" }, { - "baseAsset": "WAVES", + "baseAsset": "LTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -50515,9 +50795,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -50531,7 +50811,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18619.36144947", + "maxQty": "1042.63012644", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50545,7 +50825,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -50556,26 +50836,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "WAVESUSDC" + "status": "TRADING", + "symbol": "LTCUSDC" }, { - "baseAsset": "BCHABC", + "baseAsset": "TRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -50585,9 +50866,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -50601,7 +50882,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1100.00000000", + "maxQty": "116738.52069444", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50628,24 +50909,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BCHABCTUSD" + "symbol": "TRXPAX" }, { - "baseAsset": "BCHABC", + "baseAsset": "TRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -50655,9 +50936,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -50671,7 +50952,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "600.00000000", + "maxQty": "767789.62043085", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50698,24 +50979,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCHABCPAX" + "status": "TRADING", + "symbol": "TRXUSDC" }, { - "baseAsset": "BCHABC", + "baseAsset": "BTT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -50725,15 +51006,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -50741,7 +51022,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1700.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -50768,24 +51049,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BCHABCUSDC" + "symbol": "BTTBTC" }, { - "baseAsset": "BCHSV", + "baseAsset": "BTT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -50795,20 +51076,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "24236062.93717948", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -50832,24 +51119,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BCHSVTUSD" + "symbol": "BTTBNB" }, { - "baseAsset": "BCHSV", + "baseAsset": "BTT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -50859,9 +51146,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -50873,6 +51160,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -50896,24 +51189,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BCHSVPAX" + "symbol": "BTTUSDT" }, { - "baseAsset": "BCHSV", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -50923,9 +51216,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -50937,6 +51230,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1869.37063021", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -50960,22 +51259,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "USDS", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BCHSVUSDC" + "symbol": "BNBUSDS" }, { - "baseAsset": "LTC", + "baseAsset": "BTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -50987,9 +51286,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9000.00000000", + "minQty": "0.00000100", + "stepSize": "0.00000100" }, { "applyToMarket": true, @@ -51003,7 +51302,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "262.17698208", + "maxQty": "5.76543531", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51030,24 +51329,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDS", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LTCTUSD" + "status": "BREAK", + "symbol": "BTCUSDS" }, { - "baseAsset": "LTC", + "baseAsset": "USDS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -51057,9 +51356,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -51073,7 +51372,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "277.78825013", + "maxQty": "50936.75680055", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51100,24 +51399,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LTCPAX" + "status": "BREAK", + "symbol": "USDSUSDT" }, { - "baseAsset": "LTC", + "baseAsset": "USDS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -51127,9 +51426,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -51143,7 +51442,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1174.57092508", + "maxQty": "156600.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51170,24 +51469,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "LTCUSDC" + "status": "BREAK", + "symbol": "USDSPAX" }, { - "baseAsset": "TRX", + "baseAsset": "USDS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -51197,9 +51496,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -51213,7 +51512,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "297680.13006944", + "maxQty": "74500.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51240,24 +51539,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "TRXPAX" + "status": "BREAK", + "symbol": "USDSTUSD" }, { - "baseAsset": "TRX", + "baseAsset": "USDS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -51267,9 +51566,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -51283,7 +51582,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1459021.31180556", + "maxQty": "232600.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51315,8 +51614,8 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "TRXUSDC" + "status": "BREAK", + "symbol": "USDSUSDC" }, { "baseAsset": "BTT", @@ -51326,8 +51625,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -51345,7 +51644,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -51353,7 +51652,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8020000000.00000000", + "maxQty": "26803430.17414966", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51380,13 +51679,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BTTBTC" + "symbol": "BTTPAX" }, { "baseAsset": "BTT", @@ -51396,8 +51695,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -51415,7 +51714,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -51423,7 +51722,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "38608010.71875000", + "maxQty": "4831768.36410256", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51450,13 +51749,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BTTBNB" + "status": "BREAK", + "symbol": "BTTTUSD" }, { "baseAsset": "BTT", @@ -51466,8 +51765,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -51493,7 +51792,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "313306238.21805556", + "maxQty": "4568904.47564102", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51520,24 +51819,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BTTUSDT" + "status": "BREAK", + "symbol": "BTTUSDC" }, { - "baseAsset": "BNB", + "baseAsset": "ONG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -51547,15 +51846,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -51563,7 +51862,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1869.37063021", + "maxQty": "174204.91246537", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51590,24 +51889,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDS", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BNBUSDS" + "symbol": "ONGBNB" }, { - "baseAsset": "BTC", + "baseAsset": "ONG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -51617,15 +51916,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -51633,7 +51932,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5.76543531", + "maxQty": "88751.55594162", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51660,16 +51959,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDS", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BTCUSDS" + "status": "TRADING", + "symbol": "ONGBTC" }, { - "baseAsset": "USDS", + "baseAsset": "ONG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -51688,8 +51987,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -51703,7 +52002,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50936.75680055", + "maxQty": "201985.08617095", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51717,7 +52016,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -51728,26 +52027,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "USDSUSDT" + "status": "TRADING", + "symbol": "ONGUSDT" }, { - "baseAsset": "USDS", + "baseAsset": "HOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -51757,15 +52057,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -51773,7 +52073,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "156600.00000000", + "maxQty": "5006860.31132731", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51800,24 +52100,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "USDSPAX" + "status": "TRADING", + "symbol": "HOTBNB" }, { - "baseAsset": "USDS", + "baseAsset": "HOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -51827,9 +52127,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -51843,7 +52143,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "74500.00000000", + "maxQty": "50455925.45726198", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51857,7 +52157,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -51868,26 +52168,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "USDSTUSD" + "status": "TRADING", + "symbol": "HOTUSDT" }, { - "baseAsset": "USDS", + "baseAsset": "ZIL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -51897,9 +52198,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -51913,7 +52214,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "232600.00000000", + "maxQty": "3082687.39388464", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -51927,7 +52228,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -51938,26 +52239,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "USDSUSDC" + "status": "TRADING", + "symbol": "ZILUSDT" }, { - "baseAsset": "BTT", + "baseAsset": "ZRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -51967,15 +52269,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -51983,7 +52285,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26803430.17414966", + "maxQty": "16125.94507628", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52010,24 +52312,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BTTPAX" + "symbol": "ZRXBNB" }, { - "baseAsset": "BTT", + "baseAsset": "ZRX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -52037,7 +52339,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "900000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -52053,7 +52355,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13133535.16736111", + "maxQty": "671912.89319666", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52067,7 +52369,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -52078,26 +52380,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTTTUSD" + "symbol": "ZRXUSDT" }, { - "baseAsset": "BTT", + "baseAsset": "FET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -52107,7 +52410,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -52115,7 +52418,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -52123,7 +52426,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10977010.03680556", + "maxQty": "235252.89506601", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52150,24 +52453,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTTUSDC" + "symbol": "FETBNB" }, { - "baseAsset": "ONG", + "baseAsset": "FET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -52177,15 +52480,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -52193,7 +52496,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "174204.91246537", + "maxQty": "445955.85476025", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52207,7 +52510,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -52218,26 +52521,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ONGBNB" + "status": "TRADING", + "symbol": "FETBTC" }, { - "baseAsset": "ONG", + "baseAsset": "FET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -52247,7 +52551,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -52255,7 +52559,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -52263,7 +52567,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "191658.67777778", + "maxQty": "625030.92599027", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52277,7 +52581,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -52288,18 +52592,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONGBTC" + "symbol": "FETUSDT" }, { - "baseAsset": "ONG", + "baseAsset": "BAT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -52318,8 +52623,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -52333,7 +52638,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "127761.79288194", + "maxQty": "500154.58250868", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52347,7 +52652,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -52358,7 +52663,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -52366,18 +52672,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONGUSDT" + "symbol": "BATUSDT" }, { - "baseAsset": "HOT", + "baseAsset": "XMR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -52387,15 +52693,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -52403,7 +52709,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "27953867.14444444", + "maxQty": "1361.99391730", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52436,88 +52742,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HOTBNB" - }, - { - "baseAsset": "HOT", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "193338663.96180556", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "HOTUSDT" + "symbol": "XMRBNB" }, { - "baseAsset": "ZIL", + "baseAsset": "XMR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -52527,9 +52763,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -52543,7 +52779,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8656505.48493056", + "maxQty": "2973.96167230", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52577,18 +52813,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZILUSDT" + "symbol": "XMRUSDT" }, { - "baseAsset": "ZRX", + "baseAsset": "ZEC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -52598,15 +52834,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -52614,7 +52850,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16125.94507628", + "maxQty": "217.75958443", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52646,19 +52882,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ZRXBNB" + "status": "TRADING", + "symbol": "ZECBNB" }, { - "baseAsset": "ZRX", + "baseAsset": "ZEC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -52668,9 +52904,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -52684,7 +52920,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "925506.17083333", + "maxQty": "5710.93229881", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52718,18 +52954,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZRXUSDT" + "symbol": "ZECUSDT" }, { - "baseAsset": "FET", + "baseAsset": "ZEC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -52739,15 +52975,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -52755,7 +52991,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "224410.35694444", + "maxQty": "539.46798454", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52782,24 +53018,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "FETBNB" + "status": "BREAK", + "symbol": "ZECPAX" }, { - "baseAsset": "FET", + "baseAsset": "ZEC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -52809,15 +53045,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -52825,7 +53061,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1245632.69444444", + "maxQty": "547.97773007", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52839,7 +53075,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -52850,27 +53086,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "FETBTC" + "status": "BREAK", + "symbol": "ZECTUSD" }, { - "baseAsset": "FET", + "baseAsset": "ZEC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -52880,9 +53115,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -52896,7 +53131,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1228175.03229167", + "maxQty": "413.63028283", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -52910,7 +53145,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -52921,27 +53156,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FETUSDT" + "symbol": "ZECUSDC" }, { - "baseAsset": "BAT", + "baseAsset": "IOST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -52951,9 +53185,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -52967,7 +53201,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1266717.56544444", + "maxQty": "4548981.80750521", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53001,18 +53235,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BATUSDT" + "symbol": "IOSTUSDT" }, { - "baseAsset": "XMR", + "baseAsset": "CELR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -53022,15 +53256,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -53038,7 +53272,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "352.01334375", + "maxQty": "812572.36831132", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53071,18 +53305,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XMRBNB" + "symbol": "CELRBNB" }, { - "baseAsset": "XMR", + "baseAsset": "CELR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -53092,15 +53326,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -53108,7 +53342,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5050.52082531", + "maxQty": "5047348.25573314", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53136,24 +53370,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XMRUSDT" + "symbol": "CELRBTC" }, { - "baseAsset": "ZEC", + "baseAsset": "CELR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -53163,15 +53397,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -53179,77 +53413,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "177.82923889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZECBNB" - }, - { - "baseAsset": "ZEC", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "10268.31869023", + "maxQty": "4739395.49082696", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53283,18 +53447,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZECUSDT" + "symbol": "CELRUSDT" }, { - "baseAsset": "ZEC", + "baseAsset": "ADA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -53304,9 +53468,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -53320,7 +53484,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "539.46798454", + "maxQty": "803813.97925824", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53353,18 +53517,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ZECPAX" + "symbol": "ADAPAX" }, { - "baseAsset": "ZEC", + "baseAsset": "ADA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -53374,9 +53538,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -53390,7 +53554,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "547.97773007", + "maxQty": "588763.51084086", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53417,24 +53581,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ZECTUSD" + "status": "TRADING", + "symbol": "ADAUSDC" }, { - "baseAsset": "ZEC", + "baseAsset": "NEO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -53445,8 +53609,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -53460,7 +53624,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "384.63346600", + "maxQty": "1532.28395442", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53487,24 +53651,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZECUSDC" + "status": "BREAK", + "symbol": "NEOPAX" }, { - "baseAsset": "IOST", + "baseAsset": "NEO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -53514,15 +53678,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -53530,7 +53694,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "748849.54722222", + "maxQty": "567.60639743", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53557,95 +53721,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOSTBNB" - }, - { - "baseAsset": "IOST", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "22647444.05763889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "IOSTUSDT" + "status": "BREAK", + "symbol": "NEOUSDC" }, { - "baseAsset": "CELR", + "baseAsset": "DASH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -53655,15 +53748,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -53671,7 +53764,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "965525.77708333", + "maxQty": "665.41865879", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53704,18 +53797,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CELRBNB" + "symbol": "DASHBNB" }, { - "baseAsset": "CELR", + "baseAsset": "DASH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -53725,15 +53818,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -53741,7 +53834,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "143590750.12638889", + "maxQty": "5070.03038707", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53769,24 +53862,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CELRBTC" + "symbol": "DASHUSDT" }, { - "baseAsset": "CELR", + "baseAsset": "NANO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -53796,7 +53889,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "900000.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -53812,7 +53905,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12652741.39069444", + "maxQty": "39549.95858974", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53826,7 +53919,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -53837,19 +53930,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "CELRUSDT" + "status": "BREAK", + "symbol": "NANOUSDT" }, { - "baseAsset": "ADA", + "baseAsset": "OMG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -53875,7 +53967,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -53883,7 +53975,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "803813.97925824", + "maxQty": "40314.89527559", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53910,24 +54002,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ADAPAX" + "symbol": "OMGBNB" }, { - "baseAsset": "ADA", + "baseAsset": "OMG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -53937,7 +54029,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "900000.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -53953,7 +54045,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "198569.33215278", + "maxQty": "115408.82426685", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -53967,7 +54059,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -53978,88 +54070,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ADAUSDC" - }, - { - "baseAsset": "NEO", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1532.28395442", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "PAX", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "NEOPAX" + "symbol": "OMGUSDT" }, { - "baseAsset": "NEO", + "baseAsset": "THETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -54078,8 +54101,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -54093,7 +54116,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1242.26649792", + "maxQty": "213994.65142460", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54107,7 +54130,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54118,18 +54141,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEOUSDC" + "symbol": "THETAUSDT" }, { - "baseAsset": "DASH", + "baseAsset": "ENJ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -54148,14 +54172,14 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -54163,7 +54187,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "151.16247292", + "maxQty": "223711.54704656", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54177,7 +54201,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54188,26 +54212,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DASHBNB" + "symbol": "ENJUSDT" }, { - "baseAsset": "DASH", + "baseAsset": "MITH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -54217,9 +54242,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -54233,7 +54258,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5136.67576274", + "maxQty": "2857658.75260597", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54267,18 +54292,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DASHUSDT" + "symbol": "MITHUSDT" }, { - "baseAsset": "NANO", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -54288,15 +54313,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -54304,7 +54329,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "67475.14194444", + "maxQty": "142076.29186935", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54331,24 +54356,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NANOUSDT" + "symbol": "MATICBNB" }, { - "baseAsset": "OMG", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -54358,7 +54383,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -54366,7 +54391,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -54374,7 +54399,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "40314.89527559", + "maxQty": "516791.59715079", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54388,7 +54413,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54399,26 +54424,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "OMGBNB" + "status": "TRADING", + "symbol": "MATICBTC" }, { - "baseAsset": "OMG", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -54428,9 +54454,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -54444,7 +54470,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "151445.99094444", + "maxQty": "1630688.44190410", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54478,10 +54504,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OMGUSDT" + "symbol": "MATICUSDT" }, { - "baseAsset": "THETA", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -54500,14 +54526,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -54515,7 +54541,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "423219.67826389", + "maxQty": "3023.06070187", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54529,7 +54555,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54540,27 +54566,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "THETAUSDT" + "symbol": "ATOMBNB" }, { - "baseAsset": "ENJ", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -54570,15 +54595,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -54586,7 +54611,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "889274.62361111", + "maxQty": "13216.02355802", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54600,7 +54625,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54611,26 +54636,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ENJUSDT" + "symbol": "ATOMBTC" }, { - "baseAsset": "MITH", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -54640,9 +54666,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -54656,7 +54682,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7258505.55055556", + "maxQty": "58105.02215427", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54690,18 +54716,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MITHUSDT" + "symbol": "ATOMUSDT" }, { - "baseAsset": "MATIC", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -54711,15 +54737,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -54727,7 +54753,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "498630.04583333", + "maxQty": "5249.82095205", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54754,24 +54780,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MATICBNB" + "symbol": "ATOMUSDC" }, { - "baseAsset": "MATIC", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -54781,15 +54807,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -54797,7 +54823,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18827923.27222222", + "maxQty": "15300.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54811,7 +54837,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54822,27 +54848,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "MATICBTC" + "status": "BREAK", + "symbol": "ATOMPAX" }, { - "baseAsset": "MATIC", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -54852,9 +54877,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -54868,7 +54893,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7255938.19472222", + "maxQty": "6234.53165757", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54882,7 +54907,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -54893,27 +54918,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "MATICUSDT" + "status": "BREAK", + "symbol": "ATOMTUSD" }, { - "baseAsset": "ATOM", + "baseAsset": "ETC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -54923,15 +54947,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -54939,7 +54963,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4300.99351389", + "maxQty": "9800.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -54966,24 +54990,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ATOMBNB" + "status": "BREAK", + "symbol": "ETCUSDC" }, { - "baseAsset": "ATOM", + "baseAsset": "ETC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -54993,15 +55017,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -55009,7 +55033,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "40288.39677083", + "maxQty": "1000.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55023,7 +55047,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -55034,19 +55058,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ATOMBTC" + "status": "BREAK", + "symbol": "ETCPAX" }, { - "baseAsset": "ATOM", + "baseAsset": "ETC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -55080,7 +55103,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "81307.53800208", + "maxQty": "9100.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55094,7 +55117,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -55105,27 +55128,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ATOMUSDT" + "status": "BREAK", + "symbol": "ETCTUSD" }, { - "baseAsset": "ATOM", + "baseAsset": "BAT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -55135,9 +55157,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -55151,7 +55173,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3328.00365139", + "maxQty": "15869.81778205", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55183,19 +55205,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ATOMUSDC" + "status": "BREAK", + "symbol": "BATUSDC" }, { - "baseAsset": "ATOM", + "baseAsset": "BAT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -55205,9 +55227,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -55221,7 +55243,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15300.00000000", + "maxQty": "74639.24448563", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55254,18 +55276,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ATOMPAX" + "symbol": "BATPAX" }, { - "baseAsset": "ATOM", + "baseAsset": "BAT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -55275,9 +55297,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -55291,7 +55313,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6234.53165757", + "maxQty": "79853.08600829", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55324,18 +55346,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ATOMTUSD" + "symbol": "BATTUSD" }, { - "baseAsset": "ETC", + "baseAsset": "PHB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -55345,15 +55367,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -55361,7 +55383,77 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9800.00000000", + "maxQty": "10076970.80052493", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "PHBBNB" + }, + { + "baseAsset": "PHB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "67516.48151494", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55388,24 +55480,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ETCUSDC" + "status": "TRADING", + "symbol": "PHBBTC" }, { - "baseAsset": "ETC", + "baseAsset": "PHB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -55415,9 +55507,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -55429,12 +55521,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1000.00000000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -55458,22 +55544,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ETCPAX" + "symbol": "PHBUSDC" }, { - "baseAsset": "ETC", + "baseAsset": "PHB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -55485,9 +55571,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -55501,7 +55587,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9100.00000000", + "maxQty": "45621.10590687", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55533,89 +55619,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ETCTUSD" - }, - { - "baseAsset": "BAT", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "29337.64238889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, "status": "TRADING", - "symbol": "BATUSDC" + "symbol": "PHBTUSD" }, { - "baseAsset": "BAT", + "baseAsset": "PHB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -55625,9 +55641,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -55639,12 +55655,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "74639.24448563", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -55674,18 +55684,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BATPAX" + "symbol": "PHBPAX" }, { - "baseAsset": "BAT", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -55695,15 +55705,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -55711,7 +55721,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "79853.08600829", + "maxQty": "12572315.32734807", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55738,24 +55748,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BATTUSD" + "symbol": "TFUELBNB" }, { - "baseAsset": "PHB", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -55765,7 +55775,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -55773,7 +55783,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -55781,7 +55791,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10076970.80052493", + "maxQty": "763412.76650451", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55795,7 +55805,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -55806,26 +55816,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "PHBBNB" + "status": "TRADING", + "symbol": "TFUELBTC" }, { - "baseAsset": "PHB", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -55835,7 +55846,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -55843,7 +55854,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -55851,7 +55862,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "160924431.07222222", + "maxQty": "1365868.02849200", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -55865,7 +55876,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -55876,26 +55887,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PHBBTC" + "symbol": "TFUELUSDT" }, { - "baseAsset": "PHB", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -55906,8 +55918,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -55948,18 +55960,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PHBUSDC" + "symbol": "TFUELUSDC" }, { - "baseAsset": "PHB", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -55970,8 +55982,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -55983,12 +55995,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "2129668.27166667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -56017,19 +56023,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "PHBTUSD" + "status": "BREAK", + "symbol": "TFUELTUSD" }, { - "baseAsset": "PHB", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -56040,8 +56046,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -56082,10 +56088,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "PHBPAX" + "symbol": "TFUELPAX" }, { - "baseAsset": "TFUEL", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56111,7 +56117,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -56119,7 +56125,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12572315.32734807", + "maxQty": "630189.26268241", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -56151,11 +56157,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "TFUELBNB" + "status": "TRADING", + "symbol": "ONEBNB" }, { - "baseAsset": "TFUEL", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56189,7 +56195,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17254124.17638889", + "maxQty": "2658698.46977067", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -56223,18 +56229,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TFUELBTC" + "symbol": "ONEBTC" }, { - "baseAsset": "TFUEL", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -56245,8 +56251,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -56260,7 +56266,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5137042.84027778", + "maxQty": "3437584.60521195", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -56294,18 +56300,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TFUELUSDT" + "symbol": "ONEUSDT" }, { - "baseAsset": "TFUEL", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -56316,8 +56322,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -56352,24 +56358,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TFUELUSDC" + "symbol": "ONETUSD" }, { - "baseAsset": "TFUEL", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -56380,8 +56386,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -56416,24 +56422,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TFUELTUSD" + "symbol": "ONEPAX" }, { - "baseAsset": "TFUEL", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -56444,8 +56450,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -56457,6 +56463,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3099212.29474412", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -56480,16 +56492,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "TFUELPAX" + "symbol": "ONEUSDC" }, { - "baseAsset": "ONE", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56515,7 +56527,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -56523,7 +56535,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1131428.57916667", + "maxQty": "147031.50243224", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -56556,10 +56568,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONEBNB" + "symbol": "FTMBNB" }, { - "baseAsset": "ONE", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56593,7 +56605,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "153037121.40902778", + "maxQty": "373001.04100069", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -56627,18 +56639,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONEBTC" + "symbol": "FTMBTC" }, { - "baseAsset": "ONE", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -56649,8 +56661,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -56664,7 +56676,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15205670.01298611", + "maxQty": "1283054.97011813", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -56698,10 +56710,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONEUSDT" + "symbol": "FTMUSDT" }, { - "baseAsset": "ONE", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56762,10 +56774,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ONETUSD" + "symbol": "FTMTUSD" }, { - "baseAsset": "ONE", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56826,10 +56838,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ONEPAX" + "symbol": "FTMPAX" }, { - "baseAsset": "ONE", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56861,12 +56873,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "3099212.29474412", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -56896,10 +56902,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ONEUSDC" + "symbol": "FTMUSDC" }, { - "baseAsset": "FTM", + "baseAsset": "BTCB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -56917,26 +56923,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "336246.77986111", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -56960,158 +56960,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "FTMBNB" - }, - { - "baseAsset": "FTM", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "4285524.62013889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "FTMBTC" - }, - { - "baseAsset": "FTM", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "3149830.00090278", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "FTMUSDT" + "status": "BREAK", + "symbol": "BTCBBTC" }, { - "baseAsset": "FTM", + "baseAsset": "BCPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -57172,10 +57030,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "FTMTUSD" + "symbol": "BCPTTUSD" }, { - "baseAsset": "FTM", + "baseAsset": "BCPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -57236,10 +57094,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "FTMPAX" + "symbol": "BCPTPAX" }, { - "baseAsset": "FTM", + "baseAsset": "BCPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -57300,10 +57158,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "FTMUSDC" + "symbol": "BCPTUSDC" }, { - "baseAsset": "BTCB", + "baseAsset": "ALGO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -57321,20 +57179,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "67012.63884642", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -57358,24 +57222,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BTCBBTC" + "status": "TRADING", + "symbol": "ALGOBNB" }, { - "baseAsset": "BCPT", + "baseAsset": "ALGO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -57385,83 +57249,25 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "TUSD", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCPTTUSD" - }, - { - "baseAsset": "BCPT", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 + "filterType": "MARKET_LOT_SIZE", + "maxQty": "253683.53370396", + "minQty": "0.00000000", + "stepSize": "0.00000000" }, { "filterType": "MAX_NUM_ORDERS", @@ -57473,7 +57279,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -57484,26 +57290,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "PAX", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCPTPAX" + "status": "TRADING", + "symbol": "ALGOBTC" }, { - "baseAsset": "BCPT", + "baseAsset": "ALGO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -57513,9 +57320,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -57527,6 +57334,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "707779.16781097", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -57537,7 +57350,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -57548,15 +57361,16 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCPTUSDC" + "status": "TRADING", + "symbol": "ALGOUSDT" }, { "baseAsset": "ALGO", @@ -57566,8 +57380,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -57577,15 +57391,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -57593,7 +57407,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "64487.87055556", + "maxQty": "11220.92855555", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -57620,13 +57434,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ALGOBNB" + "status": "BREAK", + "symbol": "ALGOTUSD" }, { "baseAsset": "ALGO", @@ -57636,8 +57450,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -57647,15 +57461,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -57663,7 +57477,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "573162.34097222", + "maxQty": "76550.39262500", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -57677,7 +57491,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -57688,16 +57502,15 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ALGOBTC" + "status": "BREAK", + "symbol": "ALGOPAX" }, { "baseAsset": "ALGO", @@ -57732,12 +57545,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "815631.07960417", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -57748,7 +57555,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -57759,19 +57566,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ALGOUSDT" + "status": "BREAK", + "symbol": "ALGOUSDC" }, { - "baseAsset": "ALGO", + "baseAsset": "USDSB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -57803,12 +57609,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "8948.75531944", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -57832,16 +57632,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ALGOTUSD" + "status": "BREAK", + "symbol": "USDSBUSDT" }, { - "baseAsset": "ALGO", + "baseAsset": "USDSB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -57873,12 +57673,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "76550.39262500", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -57902,24 +57696,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDS", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ALGOPAX" + "symbol": "USDSBUSDS" }, { - "baseAsset": "ALGO", + "baseAsset": "GTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -57929,9 +57723,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -57943,6 +57737,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3384547.29312022", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -57953,7 +57753,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -57964,26 +57764,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ALGOUSDC" + "status": "TRADING", + "symbol": "GTOUSDT" }, { - "baseAsset": "USDSB", + "baseAsset": "GTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -57993,9 +57794,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -58030,24 +57831,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "USDSBUSDT" + "symbol": "GTOPAX" }, { - "baseAsset": "USDSB", + "baseAsset": "GTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -58057,9 +57858,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -58094,13 +57895,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDS", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "USDSBUSDS" + "symbol": "GTOTUSD" }, { "baseAsset": "GTO", @@ -58135,9 +57936,73 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "GTOUSDC" + }, + { + "baseAsset": "ERD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3022568.30631944", + "maxQty": "3608990.29325921", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58151,7 +58016,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -58162,27 +58027,96 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "GTOUSDT" + "status": "BREAK", + "symbol": "ERDBNB" }, { - "baseAsset": "GTO", + "baseAsset": "ERD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ERDBTC" + }, + { + "baseAsset": "ERD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -58193,8 +58127,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -58206,6 +58140,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "56447449.89854065", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -58229,24 +58169,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "GTOPAX" + "symbol": "ERDUSDT" }, { - "baseAsset": "GTO", + "baseAsset": "ERD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -58256,9 +58196,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -58293,24 +58233,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "GTOTUSD" + "symbol": "ERDPAX" }, { - "baseAsset": "GTO", + "baseAsset": "ERD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -58320,9 +58260,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -58363,10 +58303,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "GTOUSDC" + "symbol": "ERDUSDC" }, { - "baseAsset": "ERD", + "baseAsset": "DOGE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -58392,7 +58332,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -58400,7 +58340,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3608990.29325921", + "maxQty": "18003141.78146611", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58433,10 +58373,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ERDBNB" + "symbol": "DOGEBNB" }, { - "baseAsset": "ERD", + "baseAsset": "DOGE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -58470,7 +58410,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "108899160.11744267", + "maxQty": "9531772.27241139", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58484,7 +58424,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -58495,26 +58435,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ERDBTC" + "status": "TRADING", + "symbol": "DOGEBTC" }, { - "baseAsset": "ERD", + "baseAsset": "DOGE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -58540,7 +58481,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "56447449.89854065", + "maxQty": "13466681.63307852", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58554,7 +58495,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -58565,18 +58506,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ERDUSDT" + "status": "TRADING", + "symbol": "DOGEUSDT" }, { - "baseAsset": "ERD", + "baseAsset": "DOGE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -58637,10 +58579,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ERDPAX" + "symbol": "DOGEPAX" }, { - "baseAsset": "ERD", + "baseAsset": "DOGE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -58701,18 +58643,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ERDUSDC" + "symbol": "DOGEUSDC" }, { - "baseAsset": "DOGE", + "baseAsset": "DUSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -58722,15 +58664,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -58738,7 +58680,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18003141.78146611", + "maxQty": "1028455.47148704", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58771,10 +58713,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DOGEBNB" + "symbol": "DUSKBNB" }, { - "baseAsset": "DOGE", + "baseAsset": "DUSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -58808,7 +58750,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "326480665.21527778", + "maxQty": "145329.77762334", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58842,18 +58784,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOGEBTC" + "symbol": "DUSKBTC" }, { - "baseAsset": "DOGE", + "baseAsset": "DUSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -58863,7 +58805,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9222449.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -58879,7 +58821,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "47230330.93750000", + "maxQty": "602170.82140375", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -58913,18 +58855,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOGEUSDT" + "symbol": "DUSKUSDT" }, { - "baseAsset": "DOGE", + "baseAsset": "DUSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -58934,9 +58876,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -58948,6 +58890,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "760888.29311385", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -58971,24 +58919,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DOGEPAX" + "symbol": "DUSKUSDC" }, { - "baseAsset": "DOGE", + "baseAsset": "DUSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -58998,9 +58946,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -59012,6 +58960,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "833589.54538058", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59035,24 +58989,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DOGEUSDC" + "symbol": "DUSKPAX" }, { - "baseAsset": "DUSK", + "baseAsset": "BGBP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -59062,15 +59016,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -59078,7 +59032,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1028455.47148704", + "maxQty": "365798.32355803", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -59105,24 +59059,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DUSKBNB" + "symbol": "BGBPUSDC" }, { - "baseAsset": "DUSK", + "baseAsset": "ANKR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -59132,7 +59086,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -59140,7 +59094,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -59148,7 +59102,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "500493.77152778", + "maxQty": "322345.65531619", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -59175,24 +59129,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DUSKBTC" + "symbol": "ANKRBNB" }, { - "baseAsset": "DUSK", + "baseAsset": "ANKR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -59202,15 +59156,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -59218,7 +59172,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "745361.92877083", + "maxQty": "3120089.24739402", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -59232,7 +59186,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -59243,26 +59197,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DUSKUSDT" + "symbol": "ANKRBTC" }, { - "baseAsset": "DUSK", + "baseAsset": "ANKR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -59272,9 +59227,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -59288,7 +59243,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "760888.29311385", + "maxQty": "2338870.54322446", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -59302,7 +59257,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -59313,26 +59268,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DUSKUSDC" + "status": "TRADING", + "symbol": "ANKRUSDT" }, { - "baseAsset": "DUSK", + "baseAsset": "ANKR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -59342,9 +59298,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -59356,12 +59312,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "833589.54538058", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59385,24 +59335,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "DUSKPAX" + "symbol": "ANKRTUSD" }, { - "baseAsset": "BGBP", + "baseAsset": "ANKR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -59412,9 +59362,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -59426,12 +59376,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "365798.32355803", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59455,13 +59399,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BGBPUSDC" + "symbol": "ANKRPAX" }, { "baseAsset": "ANKR", @@ -59471,8 +59415,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -59490,18 +59434,12 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1121436.52986111", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59525,24 +59463,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ANKRBNB" + "status": "BREAK", + "symbol": "ANKRUSDC" }, { - "baseAsset": "ANKR", + "baseAsset": "ONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -59552,15 +59490,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -59568,7 +59506,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "137706604.75694444", + "maxQty": "23860.19880334", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -59582,7 +59520,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -59593,27 +59531,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ANKRBTC" + "status": "BREAK", + "symbol": "ONTPAX" }, { - "baseAsset": "ANKR", + "baseAsset": "ONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -59623,9 +59560,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -59637,12 +59574,6 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "10445898.06458333", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59653,7 +59584,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -59664,27 +59595,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ANKRUSDT" + "status": "BREAK", + "symbol": "ONTUSDC" }, { - "baseAsset": "ANKR", + "baseAsset": "WIN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -59694,7 +59624,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "913205152.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -59702,12 +59632,18 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "249452873.16191799", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59731,24 +59667,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ANKRTUSD" + "status": "TRADING", + "symbol": "WINBNB" }, { - "baseAsset": "ANKR", + "baseAsset": "WIN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -59758,7 +59694,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -59766,7 +59702,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -59795,24 +59731,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "PAX", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ANKRPAX" + "symbol": "WINBTC" }, { - "baseAsset": "ANKR", + "baseAsset": "WIN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -59822,7 +59758,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "913205152.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -59836,73 +59772,9 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "ANKRUSDC" - }, - { - "baseAsset": "ONT", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23860.19880334", + "maxQty": "372843732.92633773", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -59916,7 +59788,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -59927,26 +59799,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "PAX", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ONTPAX" + "status": "TRADING", + "symbol": "WINUSDT" }, { - "baseAsset": "ONT", + "baseAsset": "WIN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -59956,9 +59829,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "913205152.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -59970,6 +59843,12 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "67564419.92286309", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -59998,11 +59877,11 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ONTUSDC" + "status": "TRADING", + "symbol": "WINUSDC" }, { - "baseAsset": "WIN", + "baseAsset": "COS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -60028,7 +59907,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -60036,7 +59915,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "128068267.14652778", + "maxQty": "1848981.08825573", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60069,10 +59948,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINBNB" + "symbol": "COSBNB" }, { - "baseAsset": "WIN", + "baseAsset": "COS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -60104,73 +59983,9 @@ "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "WINBTC" - }, - { - "baseAsset": "WIN", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "401254434.86527778", + "maxQty": "35968385.30854760", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60184,7 +59999,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -60195,26 +60010,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINUSDT" + "symbol": "COSBTC" }, { - "baseAsset": "WIN", + "baseAsset": "COS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "5000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -60224,9 +60040,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "18443055.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -60240,7 +60056,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61745106.41597222", + "maxQty": "6520902.88353022", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60254,7 +60070,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -60265,26 +60081,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINUSDC" + "symbol": "COSUSDT" }, { - "baseAsset": "COS", + "baseAsset": "TUSDB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -60294,26 +60111,20 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1067089.18333333", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -60337,24 +60148,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "COSBNB" + "status": "BREAK", + "symbol": "TUSDBTUSD" }, { - "baseAsset": "COS", + "baseAsset": "NPXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -60364,7 +60175,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -60372,7 +60183,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -60380,77 +60191,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43984106.00277778", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "COSBTC" - }, - { - "baseAsset": "COS", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "5227219.86895833", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60482,141 +60223,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "COSUSDT" - }, - { - "baseAsset": "TUSDB", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "TUSD", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, "status": "BREAK", - "symbol": "TUSDBTUSD" - }, - { - "baseAsset": "NPXS", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "155101932.76666667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", "symbol": "NPXSUSDT" }, { @@ -60691,8 +60298,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -60703,14 +60310,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -60718,7 +60325,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "19537.92402778", + "maxQty": "34210.13759555", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60788,7 +60395,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2969297285.13915416", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60843,8 +60450,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -60858,7 +60465,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "153284.98100694", + "maxQty": "211949.21751216", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60901,8 +60508,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -60913,8 +60520,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -60928,7 +60535,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "146937.78956250", + "maxQty": "47324.76483669", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -60942,7 +60549,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -60953,7 +60560,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -60990,7 +60598,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -61053,8 +60661,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -61068,7 +60676,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "68155.87013889", + "maxQty": "17092.51751216", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61112,8 +60720,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -61124,8 +60732,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -61139,7 +60747,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "127085.48842361", + "maxQty": "112802.35709520", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61253,8 +60861,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -61272,7 +60880,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -61280,7 +60888,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "257862.44722222", + "maxQty": "588650.75330090", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61350,7 +60958,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1762437.43680556", + "maxQty": "899461.79986101", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61484,7 +61092,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1611957.34868056", + "maxQty": "1473959.08248783", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61527,8 +61135,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -61554,7 +61162,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "108521523.54375000", + "maxQty": "83300261.45309242", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61568,7 +61176,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -61579,7 +61187,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -61608,7 +61217,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -61624,7 +61233,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17642600.23194444", + "maxQty": "10404870.28214037", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61638,7 +61247,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -61649,7 +61258,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -61678,7 +61288,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -61694,7 +61304,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "24854787.99305556", + "maxQty": "13166698.38220986", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61748,7 +61358,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -61764,7 +61374,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "107041931.15357887", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61807,8 +61417,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -61834,7 +61444,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2168749.17430556", + "maxQty": "1519594.79708130", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61890,8 +61500,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -61905,7 +61515,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "165336.62933333", + "maxQty": "193460.07496177", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61948,8 +61558,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -61959,7 +61569,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -61975,7 +61585,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2784298.22500000", + "maxQty": "5718737.70535093", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -61989,7 +61599,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -62000,7 +61610,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -62018,8 +61629,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -62030,8 +61641,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -62045,7 +61656,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "722465.05888889", + "maxQty": "298487.09034051", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62087,7 +61698,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00001000", "tickSize": "0.00001000" }, @@ -62099,7 +61710,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -62115,7 +61726,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "99317403.02201394", + "maxQty": "19780868.48384614", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62147,7 +61758,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BTTTRX" }, { @@ -62157,9 +61768,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -62169,7 +61780,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "913205152.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -62185,7 +61796,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "133933545.13076389", + "maxQty": "61742097.23592772", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62247,7 +61858,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -62255,7 +61866,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "700481.71597222", + "maxQty": "401058.73801250", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62325,7 +61936,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "28077863.74652778", + "maxQty": "1211667.85476025", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62369,8 +61980,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -62396,7 +62007,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10611099.37430556", + "maxQty": "1890778.27178596", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62459,7 +62070,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -62467,7 +62078,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3243.50868056", + "maxQty": "3943.67073509", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62499,7 +62110,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BANDBNB" }, { @@ -62510,8 +62121,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -62537,7 +62148,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "29799.49173611", + "maxQty": "18363.76990965", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62580,9 +62191,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -62592,9 +62203,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -62608,7 +62219,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "55492.77004167", + "maxQty": "81048.83724808", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62651,9 +62262,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -62664,8 +62275,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -62679,7 +62290,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18185.73704167", + "maxQty": "7570.50559694", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62735,8 +62346,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -62750,7 +62361,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "122.97545852", + "maxQty": "91.87976913", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62805,9 +62416,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "15000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -62821,7 +62432,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10823628.24835417", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -62884,7 +62495,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -62935,8 +62546,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -62946,9 +62557,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -62962,7 +62573,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "104535.63575694", + "maxQty": "122800.99397498", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63017,8 +62628,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -63032,7 +62643,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "124848.29131250", + "maxQty": "279889.75410701", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63046,7 +62657,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -63057,7 +62668,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -63075,8 +62687,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -63094,7 +62706,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -63102,7 +62714,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6665.58062500", + "maxQty": "18683.79200833", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63145,8 +62757,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -63157,8 +62769,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -63172,7 +62784,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "111292.04036111", + "maxQty": "58820.57546907", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63216,8 +62828,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -63228,8 +62840,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -63243,7 +62855,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "312206.35438194", + "maxQty": "231430.15501042", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63287,8 +62899,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -63299,8 +62911,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -63314,7 +62926,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "451042.90416667", + "maxQty": "311821.44711605", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63385,7 +62997,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5855734.35402778", + "maxQty": "2189278.83877692", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63499,8 +63111,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -63518,7 +63130,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -63526,7 +63138,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "179832.43472222", + "maxQty": "198674.72828353", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63596,7 +63208,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3248075.75277778", + "maxQty": "829724.39124391", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63640,8 +63252,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -63652,8 +63264,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -63667,7 +63279,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3576850.40791667", + "maxQty": "1807889.36136205", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63730,7 +63342,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -63808,7 +63420,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1754293.17986111", + "maxQty": "343247.61501042", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63822,7 +63434,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -63833,7 +63445,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -63851,8 +63464,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -63863,8 +63476,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -63878,7 +63491,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1260253.37666667", + "maxQty": "449618.81862404", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -63892,7 +63505,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -63903,7 +63516,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -63921,8 +63535,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -63932,9 +63546,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -63948,7 +63562,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2017450.15006944", + "maxQty": "1140034.31688672", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64004,8 +63618,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -64019,7 +63633,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2016.22099514", + "maxQty": "1088.44517956", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64127,8 +63741,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -64139,8 +63753,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -64154,7 +63768,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4897.17966370", + "maxQty": "3866.10853984", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64197,9 +63811,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -64209,7 +63823,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "90000.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -64225,7 +63839,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "32007.20715972", + "maxQty": "36451.27365670", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64239,7 +63853,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -64250,7 +63864,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -64267,9 +63882,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -64279,7 +63894,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "90000.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -64295,7 +63910,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26841.10894444", + "maxQty": "3978.69297428", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64339,8 +63954,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -64358,7 +63973,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -64366,7 +63981,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "137593.97916667", + "maxQty": "30746.76469770", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64421,8 +64036,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -64436,7 +64051,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "401375.64166667", + "maxQty": "81201.47199444", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64450,7 +64065,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -64461,7 +64076,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -64479,8 +64095,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -64491,8 +64107,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -64506,7 +64122,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "469363.61784722", + "maxQty": "120582.65295343", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64520,7 +64136,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -64531,7 +64147,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -64568,7 +64185,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -64576,7 +64193,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6658.97972222", + "maxQty": "10369.84468380", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64619,8 +64236,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -64631,8 +64248,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -64646,7 +64263,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "42181.67152778", + "maxQty": "36696.67943015", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64690,8 +64307,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -64702,8 +64319,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -64717,7 +64334,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "113957.75535417", + "maxQty": "65119.01716469", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64788,7 +64405,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "286612.20564583", + "maxQty": "163380.29950601", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64820,7 +64437,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BUSDNGN" }, { @@ -64858,7 +64475,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "592.85165694", + "maxQty": "116.94736048", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64890,7 +64507,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "BNBNGN" }, { @@ -64900,7 +64517,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", + "maxPrice": "300109718.00000000", "minPrice": "1.00000000", "tickSize": "1.00000000" }, @@ -64912,9 +64529,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "500.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "305.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -64928,7 +64545,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11.26024454", + "maxQty": "0.25698452", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -64971,8 +64588,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -64990,7 +64607,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -64998,7 +64615,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "401341.90208333", + "maxQty": "607331.78457261", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65068,7 +64685,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5561397.92708333", + "maxQty": "1385967.85128561", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65139,7 +64756,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4337608.09611111", + "maxQty": "1633800.97477414", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65210,7 +64827,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "14592039.35861111", + "maxQty": "2709871.46136205", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65254,8 +64871,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -65266,8 +64883,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -65281,7 +64898,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "184074.53131250", + "maxQty": "102363.87894371", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65325,8 +64942,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -65336,7 +64953,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -65352,7 +64969,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8848041.78541667", + "maxQty": "2749865.75191104", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65396,8 +65013,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -65408,8 +65025,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -65423,7 +65040,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "75070.74676389", + "maxQty": "48147.64364141", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65537,8 +65154,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -65549,8 +65166,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -65564,7 +65181,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1333016.88631944", + "maxQty": "753642.77845726", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65608,8 +65225,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -65619,7 +65236,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "900000.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -65635,7 +65252,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2030779.16472222", + "maxQty": "1205871.53242529", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65698,7 +65315,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -65776,7 +65393,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "370581.90277778", + "maxQty": "208848.08686587", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -65790,7 +65407,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -65801,7 +65418,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -65828,11 +65446,223 @@ "multiplierDown": "0.2", "multiplierUp": "5" }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "343932.80194579", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTXCUSDT" + }, + { + "baseAsset": "BCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "412.37087143", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BCHBNB" + }, + { + "baseAsset": "BCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1151.81571994", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BCHBTC" + }, + { + "baseAsset": "BCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -65846,147 +65676,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "334293.84834722", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "CTXCUSDT" - }, - { - "baseAsset": "BCH", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "129.53386944", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "BCHBNB" - }, - { - "baseAsset": "BCH", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "2675.43492222", + "maxQty": "3047.44742218", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66014,13 +65704,13 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BCHBTC" + "symbol": "BCHUSDT" }, { "baseAsset": "BCH", @@ -66030,8 +65720,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -66042,8 +65732,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -66057,7 +65747,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3977.76908617", + "maxQty": "258.93106646", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66071,7 +65761,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -66082,16 +65772,15 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BCHUSDT" + "symbol": "BCHUSDC" }, { "baseAsset": "BCH", @@ -66101,8 +65790,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -66113,8 +65802,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -66128,7 +65817,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "150.69233706", + "maxQty": "70.34291163", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66155,13 +65844,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDC", + "quoteAsset": "TUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BCHUSDC" + "status": "BREAK", + "symbol": "BCHTUSD" }, { "baseAsset": "BCH", @@ -66198,7 +65887,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "80.86035688", + "maxQty": "43.26778509", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66225,13 +65914,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TUSD", + "quoteAsset": "PAX", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BCHTUSD" + "status": "BREAK", + "symbol": "BCHPAX" }, { "baseAsset": "BCH", @@ -66241,8 +65930,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -66253,8 +65942,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -66268,7 +65957,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13.18264471", + "maxQty": "758.41185324", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66282,7 +65971,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -66293,26 +65982,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "PAX", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BCHPAX" + "symbol": "BCHBUSD" }, { - "baseAsset": "BCH", + "baseAsset": "BTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "100000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -66322,7 +66012,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "900.00000000", "minQty": "0.00001000", "stepSize": "0.00001000" }, @@ -66330,7 +66020,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -66338,7 +66028,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1071.14883799", + "maxQty": "6.21026229", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66352,7 +66042,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -66363,27 +66053,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BCHBUSD" + "symbol": "BTCRUB" }, { - "baseAsset": "BTC", + "baseAsset": "ETH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "4000000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -66393,9 +66082,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "23055.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -66409,7 +66098,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3.63844403", + "maxQty": "50.00571223", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66442,18 +66131,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCRUB" + "symbol": "ETHRUB" }, { - "baseAsset": "ETH", + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.10000000", - "tickSize": "0.10000000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -66463,9 +66152,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -66479,7 +66168,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "157.61244139", + "maxQty": "41461.80681028", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66512,18 +66201,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHRUB" + "symbol": "XRPRUB" }, { - "baseAsset": "XRP", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "500000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -66533,9 +66222,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "184466.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -66549,77 +66238,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "308566.43416667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "RUB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "XRPRUB" - }, - { - "baseAsset": "BNB", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "3320.12679167", + "maxQty": "162.14310006", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66662,8 +66281,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -66673,7 +66292,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "90000000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -66681,7 +66300,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -66689,7 +66308,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2125480.49097222", + "maxQty": "3561972.57470465", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66743,7 +66362,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -66759,7 +66378,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "220702888.31458333", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66792,7 +66411,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "TROYBTC" }, { @@ -66803,8 +66422,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -66814,7 +66433,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -66830,7 +66449,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8844421.01666667", + "maxQty": "9021749.52189020", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66873,21 +66492,21 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "112.38300000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -66901,7 +66520,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "116076.33506944", + "maxQty": "182842.23203613", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -66956,8 +66575,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -66971,7 +66590,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43695.35440347", + "maxQty": "15410.81737317", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67014,8 +66633,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -67026,8 +66645,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -67041,7 +66660,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9584332.06666667", + "maxQty": "5724987.80576789", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67104,7 +66723,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -67182,7 +66801,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2404857.63125000", + "maxQty": "1217204.63099374", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67252,7 +66871,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "991946.89534722", + "maxQty": "975576.88707435", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67294,7 +66913,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00010000", "tickSize": "0.00010000" }, @@ -67306,7 +66925,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -67314,7 +66933,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -67322,7 +66941,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "52409.97445833", + "maxQty": "4304.89061153", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67392,7 +67011,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "60759.98848611", + "maxQty": "11607.07902710", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67435,8 +67054,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -67447,8 +67066,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -67462,7 +67081,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17602.88818681", + "maxQty": "25170.73571230", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67476,7 +67095,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -67487,7 +67106,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -67517,8 +67137,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -67532,7 +67152,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18.35695944", + "maxQty": "13.95142151", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67574,9 +67194,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -67586,9 +67206,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92232.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -67602,7 +67222,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2796.06415278", + "maxQty": "252.01051772", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67644,21 +67264,21 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00100000", + "maxPrice": "22.60000000", + "minPrice": "4.34900000", "tickSize": "0.00100000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -67672,7 +67292,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "364686.98149306", + "maxQty": "922327.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67714,9 +67334,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "600000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -67726,9 +67346,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "45000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "153719.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -67742,7 +67362,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "439.48545367", + "maxQty": "124.79740069", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67784,7 +67404,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -67796,9 +67416,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -67812,7 +67432,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "181408.18495139", + "maxQty": "95020.05142460", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67854,21 +67474,21 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00100000", + "maxPrice": "22.60000000", + "minPrice": "4.34900000", "tickSize": "0.00100000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -67882,7 +67502,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "974811.43267361", + "maxQty": "3094774.72967338", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -67924,21 +67544,21 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "112.40200000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -67952,7 +67572,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "407627.32125000", + "maxQty": "842838.44072272", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68007,77 +67627,6 @@ { "filterType": "LOT_SIZE", "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "87.02498521", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "EUR", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "BTCEUR" - }, - { - "baseAsset": "ETH", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", "minQty": "0.00001000", "stepSize": "0.00001000" }, @@ -68093,7 +67642,78 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1119.94344936", + "maxQty": "36.93172677", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCEUR" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "270.50215531", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68136,9 +67756,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -68148,7 +67768,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -68164,7 +67784,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8951.53223333", + "maxQty": "750.31977261", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68207,8 +67827,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -68218,9 +67838,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -68234,7 +67854,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "864331.12562500", + "maxQty": "302177.38082001", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68276,21 +67896,21 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1.74360000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -68304,7 +67924,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1066677.28858333", + "maxQty": "1826406.98692842", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68347,21 +67967,21 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1.74330000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "6000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -68375,7 +67995,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1435676.53075694", + "maxQty": "2531762.47050729", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68419,8 +68039,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -68431,14 +68051,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -68446,7 +68066,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48441.97368056", + "maxQty": "110870.16400277", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68516,7 +68136,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "431193.43333333", + "maxQty": "246827.16608756", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68530,7 +68150,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -68541,7 +68161,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -68571,8 +68192,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -68586,7 +68207,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "757873.24077083", + "maxQty": "432960.88325225", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68600,7 +68221,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -68611,7 +68232,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -68648,7 +68270,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -68726,7 +68348,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "265551707.70833333", + "maxQty": "40535.61014593", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -68769,8 +68391,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -68780,7 +68402,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -68796,7 +68418,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8792824.31875000", + "maxQty": "105722.05291730", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69418,7 +69040,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -69496,7 +69118,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20770350.32361111", + "maxQty": "6265185.48019457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69540,8 +69162,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -69567,7 +69189,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3239308.31180556", + "maxQty": "3161935.65809589", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69623,14 +69245,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -69638,7 +69260,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "74033.28819444", + "maxQty": "24620.28895066", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69693,8 +69315,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -69708,7 +69330,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "352757.98263889", + "maxQty": "73543.95107713", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69751,8 +69373,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -69762,7 +69384,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "900000.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -69778,7 +69400,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "438292.21687500", + "maxQty": "199072.25405142", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69821,8 +69443,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -69833,8 +69455,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -69848,7 +69470,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "196084.47783333", + "maxQty": "113463.64280750", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69862,7 +69484,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -69873,7 +69495,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -69918,7 +69541,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1705236.60312500", + "maxQty": "2326619.99972202", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -69932,7 +69555,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -69943,7 +69566,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -70031,8 +69655,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -70043,8 +69667,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -70058,7 +69682,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "60827.75317361", + "maxQty": "40527.86740792", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70101,8 +69725,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -70113,8 +69737,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -70128,7 +69752,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63238.26716667", + "maxQty": "34763.07403057", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70172,8 +69796,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -70184,8 +69808,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -70199,7 +69823,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "45779.11947917", + "maxQty": "12334.69223071", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70261,7 +69885,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -70339,7 +69963,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "390417.80972222", + "maxQty": "228216.56636553", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70383,8 +70007,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -70395,8 +70019,8 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -70410,7 +70034,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "227748.33118056", + "maxQty": "397744.80125086", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70454,8 +70078,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -70466,8 +70090,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -70481,7 +70105,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25155.96375000", + "maxQty": "20511.60405837", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70495,7 +70119,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -70506,7 +70130,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -70524,8 +70149,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -70536,8 +70161,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -70551,7 +70176,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1665.95350849", + "maxQty": "946.69610038", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70595,8 +70220,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -70607,8 +70232,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -70622,7 +70247,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11005.67639931", + "maxQty": "4747.92290479", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70665,9 +70290,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -70677,7 +70302,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "90000.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -70693,7 +70318,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26230.45456944", + "maxQty": "8482.60779708", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70736,8 +70361,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -70748,8 +70373,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -70763,7 +70388,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "75838.68420833", + "maxQty": "41302.47512161", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -70806,7 +70431,7 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000007.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -70818,7 +70443,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "92232.00000000", "minQty": "0.00001000", "stepSize": "0.00001000" }, @@ -70834,7 +70459,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "158196.62795554", + "maxQty": "92232.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -71379,8 +71004,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -71394,7 +71019,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "419780.28170833", + "maxQty": "119281.23994440", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -71408,7 +71033,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -71419,7 +71044,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -71437,8 +71063,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -71448,7 +71074,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "900000.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -71464,7 +71090,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "391546.24194444", + "maxQty": "91215.64079221", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -71478,7 +71104,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -71489,7 +71115,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -71507,8 +71134,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -71519,8 +71146,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -71534,7 +71161,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "53093.30412500", + "maxQty": "18987.22217948", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -71566,7 +71193,7 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", + "status": "BREAK", "symbol": "NANOBUSD" }, { @@ -71589,8 +71216,8 @@ { "filterType": "LOT_SIZE", "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -71604,7 +71231,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "187468.88053472", + "maxQty": "77252.49617790", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -71618,7 +71245,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -71629,7 +71256,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -71674,7 +71302,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3867609.06152778", + "maxQty": "876251.41320361", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -71806,7 +71434,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -72008,9 +71636,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -72024,7 +71652,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1583709.34809722", + "maxQty": "818850.58683113", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72059,76 +71687,6 @@ "status": "TRADING", "symbol": "AIONUSDT" }, - { - "baseAsset": "MBL", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "8407879.18541667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "MBLBNB" - }, { "baseAsset": "MBL", "baseAssetPrecision": 8, @@ -72156,77 +71714,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "296722996.35416667", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "MBLBTC" - }, - { - "baseAsset": "MBL", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -72234,77 +71722,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17968414.50972222", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "MBLUSDT" - }, - { - "baseAsset": "COTI", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "265223.77847222", + "maxQty": "2237310.65253648", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72337,10 +71755,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "COTIBNB" + "symbol": "MBLBNB" }, { - "baseAsset": "COTI", + "baseAsset": "MBL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -72358,7 +71776,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -72374,7 +71792,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2591979.45138889", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72388,7 +71806,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -72399,16 +71817,85 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, + "status": "BREAK", + "symbol": "MBLBTC" + }, + { + "baseAsset": "MBL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6804227.96942321", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, "status": "TRADING", - "symbol": "COTIBTC" + "symbol": "MBLUSDT" }, { "baseAsset": "COTI", @@ -72418,8 +71905,8 @@ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -72430,14 +71917,14 @@ { "filterType": "LOT_SIZE", "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -72445,7 +71932,77 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3246275.13472222", + "maxQty": "202830.59277275", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "COTIBNB" + }, + { + "baseAsset": "COTI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "419450.80542043", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72473,16 +72030,16 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "COTIUSDT" + "symbol": "COTIBTC" }, { - "baseAsset": "ALGO", + "baseAsset": "COTI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -72500,9 +72057,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -72516,7 +72073,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "405421.61758333", + "maxQty": "960239.38151494", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72544,24 +72101,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ALGOBUSD" + "symbol": "COTIUSDT" }, { - "baseAsset": "BTT", + "baseAsset": "ALGO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -72571,7 +72128,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "900000.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -72587,7 +72144,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "281733549.90138889", + "maxQty": "216303.21881862", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72601,7 +72158,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -72612,7 +72169,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -72620,18 +72178,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTTBUSD" + "symbol": "ALGOBUSD" }, { - "baseAsset": "TOMO", + "baseAsset": "BTT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -72641,9 +72199,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -72657,7 +72215,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "77792.56304167", + "maxQty": "45750289.43461538", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72689,19 +72247,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "TOMOBUSD" + "status": "BREAK", + "symbol": "BTTBUSD" }, { - "baseAsset": "XMR", + "baseAsset": "TOMO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -72711,9 +72269,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -72727,7 +72285,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1624.36871563", + "maxQty": "24389.52633773", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72741,7 +72299,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -72752,8 +72310,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -72761,18 +72318,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XMRBUSD" + "symbol": "TOMOBUSD" }, { - "baseAsset": "ZEC", + "baseAsset": "XMR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -72783,8 +72340,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -72798,7 +72355,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1996.41258493", + "maxQty": "630.00956870", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72812,7 +72369,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -72823,7 +72380,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -72831,18 +72389,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZECBUSD" + "symbol": "XMRBUSD" }, { - "baseAsset": "BNBBULL", + "baseAsset": "ZEC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -72853,8 +72411,8 @@ { "filterType": "LOT_SIZE", "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -72868,7 +72426,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8211.42395395", + "maxQty": "1874.44013968", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -72895,13 +72453,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BNBBULLUSDT" + "status": "TRADING", + "symbol": "ZECBUSD" }, { "baseAsset": "BNBBULL", @@ -72938,7 +72496,77 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3671.17078985", + "maxQty": "8211.42395395", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BNBBULLUSDT" + }, + { + "baseAsset": "BNBBULL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3671.17078985", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73035,24 +72663,48271 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BNBBEARUSDT" + }, + { + "baseAsset": "BNBBEAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2323.69823608", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BNBBEARBUSD" + }, + { + "baseAsset": "STPT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "389641.21603261", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "STPTBNB" + }, + { + "baseAsset": "STPT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "872103.55455177", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STPTBTC" + }, + { + "baseAsset": "STPT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "977786.62453092", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STPTUSDT" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10009085.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9214.00000000", + "minQty": "0.00000100", + "stepSize": "0.00000100" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "0.09357322", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ZAR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTCZAR" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4.56081497", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ZAR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ETHZAR" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "50.34538889", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ZAR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BNBZAR" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "17427.60451389", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ZAR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "USDTZAR" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7349.00430556", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ZAR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BUSDZAR" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "700000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100.00000000", + "minQty": "0.00000100", + "stepSize": "0.00000100" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6.47489095", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTCBKRW" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "8000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "34.29793919", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ETHBKRW" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "5000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "471.04390139", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BNBBKRW" + }, + { + "baseAsset": "WTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "186155.95830437", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WTCUSDT" + }, + { + "baseAsset": "DATA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "464811.90736622", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DATABUSD" + }, + { + "baseAsset": "DATA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1116673.92793606", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DATAUSDT" + }, + { + "baseAsset": "XZC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "18526.19352431", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XZCUSDT" + }, + { + "baseAsset": "SOL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4891.38476025", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SOLBNB" + }, + { + "baseAsset": "SOL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8223.17167477", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SOLBTC" + }, + { + "baseAsset": "SOL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "46233.60564280", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SOLUSDT" + }, + { + "baseAsset": "SOL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16863.21426685", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SOLBUSD" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "9000000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "0.59662685", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "IDRT", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "BREAK", + "symbol": "BTCIDRT" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "50000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "46.31829742", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "IDRT", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "BNBIDRT" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "21856.00", + "minPrice": "7285.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "82227.87113273", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "IDRT", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "USDTIDRT" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "50855.40449235", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "IDRT", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "BREAK", + "symbol": "BUSDIDRT" + }, + { + "baseAsset": "CTSI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "164471.30715774", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTSIBTC" + }, + { + "baseAsset": "CTSI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "366530.51841556", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTSIUSDT" + }, + { + "baseAsset": "CTSI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "174916.03266157", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTSIBNB" + }, + { + "baseAsset": "CTSI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "154526.76872828", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTSIBUSD" + }, + { + "baseAsset": "HIVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "167070.80693481", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "HIVEBNB" + }, + { + "baseAsset": "HIVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "48436.54551772", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HIVEBTC" + }, + { + "baseAsset": "HIVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "91082.55038220", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HIVEUSDT" + }, + { + "baseAsset": "CHR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "200200.78804725", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHRBNB" + }, + { + "baseAsset": "CHR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "289352.66921473", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHRBTC" + }, + { + "baseAsset": "CHR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "754977.65531619", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHRUSDT" + }, + { + "baseAsset": "BTCUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "67.93300000", + "minPrice": "3.57600000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "3000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3000.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "140.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCUPUSDT" + }, + { + "baseAsset": "BTCDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.03082900", + "minPrice": "0.00162300", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "921415.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "921415.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "308414.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCDOWNUSDT" + }, + { + "baseAsset": "GXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "104202.54899235", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GXSUSDT" + }, + { + "baseAsset": "ARDR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "310042.97984711", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ARDRUSDT" + }, + { + "baseAsset": "ERD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7251597.19596942", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ERDBUSD" + }, + { + "baseAsset": "LEND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1798891.90756944", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LENDUSDT" + }, + { + "baseAsset": "HBAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "793878.42974287", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HBARBUSD" + }, + { + "baseAsset": "MATIC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "488438.69388464", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MATICBUSD" + }, + { + "baseAsset": "WRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "52465.47826963", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WRXBUSD" + }, + { + "baseAsset": "ZIL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1678086.72133425", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ZILBUSD" + }, + { + "baseAsset": "MDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "336429.52026144", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "MDTBNB" + }, + { + "baseAsset": "MDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1384833.17442668", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MDTBTC" + }, + { + "baseAsset": "MDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2007419.43835997", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MDTUSDT" + }, + { + "baseAsset": "STMX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "28430629.91243919", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STMXBTC" + }, + { + "baseAsset": "STMX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2155095.08617095", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STMXETH" + }, + { + "baseAsset": "STMX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5414740.87004864", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STMXUSDT" + }, + { + "baseAsset": "KNC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "44135.44051424", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KNCBUSD" + }, + { + "baseAsset": "KNC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "121824.62237665", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KNCUSDT" + }, + { + "baseAsset": "REP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9858.55033818", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "REPBUSD" + }, + { + "baseAsset": "REP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3850.69419041", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "REPUSDT" + }, + { + "baseAsset": "LRC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "197625.75121612", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LRCBUSD" + }, + { + "baseAsset": "LRC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "604080.16052814", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LRCUSDT" + }, + { + "baseAsset": "IQ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1799012.17303683", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IQBNB" + }, + { + "baseAsset": "IQ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1152365.32036136", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IQBUSD" + }, + { + "baseAsset": "PNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "46153.89298123", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PNTBTC" + }, + { + "baseAsset": "PNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "137241.40741487", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PNTUSDT" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "13.26507321", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCGBP" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "126.78981507", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHGBP" + }, + { + "baseAsset": "XRP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "176965.54134815", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XRPGBP" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "242.61047831", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBGBP" + }, + { + "baseAsset": "GBP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "2.07820000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "350756.06029186", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GBPBUSD" + }, + { + "baseAsset": "DGB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21665004.19735927", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DGBBTC" + }, + { + "baseAsset": "DGB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "680456.10076441", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DGBBUSD" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "51212504.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1800.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2.86723802", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "UAH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCUAH" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "41.22400000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "252553.35510771", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "UAH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "USDTUAH" + }, + { + "baseAsset": "COMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "531.50417095", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "COMPBTC" + }, + { + "baseAsset": "COMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "51.10757222", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "COMPBNB" + }, + { + "baseAsset": "COMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "817.42610354", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "COMPBUSD" + }, + { + "baseAsset": "COMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3192.02405281", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "COMPUSDT" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "9000000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8.73589900", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "BTCBIDR" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "900000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "80.00215025", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "ETHBIDR" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "50000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "325.76480542", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "BNBBIDR" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "22032.00", + "minPrice": "7344.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "600834.77984016", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "BUSDBIDR" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "22035.00", + "minPrice": "7345.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1179931.60602501", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "USDTBIDR" + }, + { + "baseAsset": "BKRW", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "61102615.56557946", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BKRWUSDT" + }, + { + "baseAsset": "BKRW", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "48969448.15395284", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BKRWBUSD" + }, + { + "baseAsset": "SC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10196812.90548992", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SCUSDT" + }, + { + "baseAsset": "ZEN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7491.33371091", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ZENUSDT" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "100749.74273801", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPBTC" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "24170.67317581", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPBNB" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "39376.11812369", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPBUSD" + }, + { + "baseAsset": "SNX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12893.15225851", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SNXBTC" + }, + { + "baseAsset": "SNX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3561.24169562", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SNXBNB" + }, + { + "baseAsset": "SNX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12896.68028075", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SNXBUSD" + }, + { + "baseAsset": "SNX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "41125.58075052", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SNXUSDT" + }, + { + "baseAsset": "ETHUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "46.99700000", + "minPrice": "2.47400000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "3000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3000.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "202.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHUPUSDT" + }, + { + "baseAsset": "ETHDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "2.08190000", + "minPrice": "0.10960000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "78063.27061153", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "4554.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHDOWNUSDT" + }, + { + "baseAsset": "ADAUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "13.66800000", + "minPrice": "0.72000000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8547.86218902", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "348.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADAUPUSDT" + }, + { + "baseAsset": "ADADOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.01497800", + "minPrice": "0.00078900", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7280255.57404447", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "317380.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADADOWNUSDT" + }, + { + "baseAsset": "LINKUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.23003000", + "minPrice": "0.01211000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "481581.88611535", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "20627.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKUPUSDT" + }, + { + "baseAsset": "LINKDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.02753000", + "minPrice": "0.00144900", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "300000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "300000.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "171233.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKDOWNUSDT" + }, + { + "baseAsset": "VTHO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12132373.67129951", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VTHOBNB" + }, + { + "baseAsset": "VTHO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "37909546.95988935", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "VTHOBUSD" + }, + { + "baseAsset": "VTHO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "26377891.35441278", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VTHOUSDT" + }, + { + "baseAsset": "DCR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "967.25717012", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DCRBUSD" + }, + { + "baseAsset": "DGB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3465302.09485753", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DGBUSDT" + }, + { + "baseAsset": "GBP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "2.07870000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "544044.50979847", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GBPUSDT" + }, + { + "baseAsset": "STORJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222448.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "31346.71924947", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STORJBUSD" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "167236.56712995", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPUSDT" + }, + { + "baseAsset": "IRIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "85086.21045752", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "IRISBNB" + }, + { + "baseAsset": "IRIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "732498.70604586", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IRISBTC" + }, + { + "baseAsset": "IRIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "78319.48723994", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "IRISBUSD" + }, + { + "baseAsset": "MKR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10.44837309", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "MKRBNB" + }, + { + "baseAsset": "MKR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "35.96341077", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MKRBTC" + }, + { + "baseAsset": "MKR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "84.73052633", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MKRUSDT" + }, + { + "baseAsset": "MKR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "46.48398011", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MKRBUSD" + }, + { + "baseAsset": "DAI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "13225.87375000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DAIBNB" + }, + { + "baseAsset": "DAI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "64544.98819444", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DAIBTC" + }, + { + "baseAsset": "DAI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "452151.67404167", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DAIUSDT" + }, + { + "baseAsset": "DAI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "119396.14040278", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DAIBUSD" + }, + { + "baseAsset": "RUNE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "22040.95691452", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RUNEBNB" + }, + { + "baseAsset": "RUNE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "27451.53502432", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RUNEBTC" + }, + { + "baseAsset": "RUNE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "32249.87956914", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RUNEBUSD" + }, + { + "baseAsset": "MANA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "175404.69492703", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MANABUSD" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4140074.95489923", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGEBUSD" + }, + { + "baseAsset": "LEND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "270430.73030556", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LENDBUSD" + }, + { + "baseAsset": "ZRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "71870.78109798", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ZRXBUSD" + }, + { + "baseAsset": "DCR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1321.22915427", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DCRUSDT" + }, + { + "baseAsset": "STORJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "137096.35015983", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STORJUSDT" + }, + { + "baseAsset": "XRP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "29020.38854167", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XRPBKRW" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "101738.55506944", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ADABKRW" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11.53392674", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCAUD" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "88.98189770", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHAUD" + }, + { + "baseAsset": "AUD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.09710000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "892617.95093815", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUDBUSD" + }, + { + "baseAsset": "FIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "224752.86935371", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FIOBNB" + }, + { + "baseAsset": "FIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "329622.76511466", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FIOBTC" + }, + { + "baseAsset": "FIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "186735.04378040", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FIOBUSD" + }, + { + "baseAsset": "BNBUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "304.45000000", + "minPrice": "16.03000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "500.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "500.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "31.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBUPUSDT" + }, + { + "baseAsset": "BNBDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.22081000", + "minPrice": "0.01163000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "277647.09416261", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "42849.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBDOWNUSDT" + }, + { + "baseAsset": "XTZUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.00492800", + "minPrice": "0.00026000", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3999901.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XTZUPUSDT" + }, + { + "baseAsset": "XTZDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "4.91690000", + "minPrice": "0.25880000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "49991176.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5261.19938461", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XTZDOWNUSDT" + }, + { + "baseAsset": "AVA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "23390.13516330", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVABNB" + }, + { + "baseAsset": "AVA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "45323.87764419", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVABTC" + }, + { + "baseAsset": "AVA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "17179.05086865", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVABUSD" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "37891.35378735", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "USDTBKRW" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "112393.11979167", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BUSDBKRW" + }, + { + "baseAsset": "IOTA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "102853.33478804", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IOTABUSD" + }, + { + "baseAsset": "MANA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "506694.47116052", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MANAUSDT" + }, + { + "baseAsset": "XRP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "126502.91452397", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XRPAUD" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "350.32371785", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBAUD" + }, + { + "baseAsset": "AUD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.09740000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2149694.83154968", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUDUSDT" + }, + { + "baseAsset": "BAL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "174.62016644", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BALBNB" + }, + { + "baseAsset": "BAL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3800.09968033", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BALBTC" + }, + { + "baseAsset": "BAL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2231.49403057", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BALBUSD" + }, + { + "baseAsset": "YFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "0.96662995", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "YFIBNB" + }, + { + "baseAsset": "YFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6.96412735", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIBTC" + }, + { + "baseAsset": "YFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4.65784281", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIBUSD" + }, + { + "baseAsset": "YFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15.76439332", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIUSDT" + }, + { + "baseAsset": "BLZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "346951.95656706", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BLZBUSD" + }, + { + "baseAsset": "KMD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "55086.92722724", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "KMDBUSD" + }, + { + "baseAsset": "BAL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7558.39727588", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BALUSDT" + }, + { + "baseAsset": "BLZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "842502.75608061", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BLZUSDT" + }, + { + "baseAsset": "IRIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1048860.25580264", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IRISUSDT" + }, + { + "baseAsset": "KMD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "195631.72662265", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KMDUSDT" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11.05204413", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "DAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCDAI" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "127.63607720", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "DAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHDAI" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "241.87445309", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "DAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBDAI" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.29850000", + "minPrice": "0.69920000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "969160.63981931", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "DAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "USDTDAI" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.29890000", + "minPrice": "0.69940000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1167544.06646282", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "DAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BUSDDAI" + }, + { + "baseAsset": "JST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "319948.32777777", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "JSTBNB" + }, + { + "baseAsset": "JST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1107015.59068797", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "JSTBTC" + }, + { + "baseAsset": "JST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "991777.29749826", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "JSTBUSD" + }, + { + "baseAsset": "JST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2658316.09666435", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "JSTUSDT" + }, + { + "baseAsset": "SRM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "14283.41181375", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SRMBNB" + }, + { + "baseAsset": "SRM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "26759.87407922", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SRMBTC" + }, + { + "baseAsset": "SRM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "35809.75865184", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SRMBUSD" + }, + { + "baseAsset": "SRM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "64977.42314107", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SRMUSDT" + }, + { + "baseAsset": "ANT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4692.97623349", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ANTBNB" + }, + { + "baseAsset": "ANT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9102.33620569", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ANTBTC" + }, + { + "baseAsset": "ANT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16568.05587213", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ANTBUSD" + }, + { + "baseAsset": "ANT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "49689.42369701", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ANTUSDT" + }, + { + "baseAsset": "CRV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21614.27031900", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "CRVBNB" + }, + { + "baseAsset": "CRV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "62985.51035441", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CRVBTC" + }, + { + "baseAsset": "CRV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "60505.96421125", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CRVBUSD" + }, + { + "baseAsset": "CRV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "174877.10090340", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CRVUSDT" + }, + { + "baseAsset": "SAND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "51501.11883252", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SANDBNB" + }, + { + "baseAsset": "SAND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "162769.74843641", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SANDBTC" + }, + { + "baseAsset": "SAND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "752456.27866574", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SANDUSDT" + }, + { + "baseAsset": "SAND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "270382.20291869", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SANDBUSD" + }, + { + "baseAsset": "OCEAN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "48985.30229325", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OCEANBNB" + }, + { + "baseAsset": "OCEAN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "62611.68589298", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OCEANBTC" + }, + { + "baseAsset": "OCEAN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "91648.34607366", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OCEANBUSD" + }, + { + "baseAsset": "OCEAN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "196951.80611535", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OCEANUSDT" + }, + { + "baseAsset": "NMR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1515.61734537", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NMRBTC" + }, + { + "baseAsset": "NMR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1181.73106323", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NMRBUSD" + }, + { + "baseAsset": "NMR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4609.45814454", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NMRUSDT" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10812.43567755", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTBNB" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "40950.35675469", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTBTC" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "40611.14321264", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTBUSD" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "118827.86774843", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTUSDT" + }, + { + "baseAsset": "LUNA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8125.17923558", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LUNABNB" + }, + { + "baseAsset": "LUNA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "20083.94153578", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LUNABTC" + }, + { + "baseAsset": "LUNA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "23732.54018068", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LUNABUSD" + }, + { + "baseAsset": "LUNA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "62231.31089645", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LUNAUSDT" + }, + { + "baseAsset": "IDEX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "407452.36136205", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IDEXBTC" + }, + { + "baseAsset": "IDEX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "580279.30583738", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IDEXBUSD" + }, + { + "baseAsset": "RSR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1719455.71021542", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RSRBNB" + }, + { + "baseAsset": "RSR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "35808850.42946490", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RSRBTC" + }, + { + "baseAsset": "RSR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2816108.49228630", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RSRBUSD" + }, + { + "baseAsset": "RSR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9341746.64162612", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RSRUSDT" + }, + { + "baseAsset": "PAXG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21.62215726", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PAXGBNB" + }, + { + "baseAsset": "PAXG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "107.36844280", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PAXGBTC" + }, + { + "baseAsset": "PAXG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00000100", + "stepSize": "0.00000100" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "49.69813487", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "PAXGBUSD" + }, + { + "baseAsset": "PAXG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1115.24166791", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PAXGUSDT" + }, + { + "baseAsset": "WNXM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "365.95426629", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "WNXMBNB" + }, + { + "baseAsset": "WNXM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "585.74215427", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WNXMBTC" + }, + { + "baseAsset": "WNXM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "271.96067313", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "WNXMBUSD" + }, + { + "baseAsset": "WNXM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1615.65364836", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WNXMUSDT" + }, + { + "baseAsset": "TRB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "248.28392917", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "TRBBNB" + }, + { + "baseAsset": "TRB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1854.08149409", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRBBTC" + }, + { + "baseAsset": "TRB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1457.37319666", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRBBUSD" + }, + { + "baseAsset": "TRB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4045.76002779", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRBUSDT" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "39.72135211", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ETHNGN" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "50000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2537.02104239", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "DOTBIDR" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2889.66913759", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKAUD" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3152.10402777", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SXPAUD" + }, + { + "baseAsset": "BZRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "32864.14458333", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BZRXBNB" + }, + { + "baseAsset": "BZRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "509775.21538461", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BZRXBTC" + }, + { + "baseAsset": "BZRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "348077.62192307", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BZRXBUSD" + }, + { + "baseAsset": "BZRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1819638.33974358", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BZRXUSDT" + }, + { + "baseAsset": "WBTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "250.17480157", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WBTCBTC" + }, + { + "baseAsset": "WBTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3.54955495", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WBTCETH" + }, + { + "baseAsset": "SUSHI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "14633.80152883", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUSHIBNB" + }, + { + "baseAsset": "SUSHI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "39246.07011813", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUSHIBTC" + }, + { + "baseAsset": "SUSHI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "31415.06578179", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUSHIBUSD" + }, + { + "baseAsset": "SUSHI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "85524.80535093", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUSHIUSDT" + }, + { + "baseAsset": "YFII", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6.59098943", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIIBNB" + }, + { + "baseAsset": "YFII", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "17.85390736", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIIBTC" + }, + { + "baseAsset": "YFII", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11.06799803", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIIBUSD" + }, + { + "baseAsset": "YFII", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "52.31040475", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIIUSDT" + }, + { + "baseAsset": "KSM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "209.20497845", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KSMBNB" + }, + { + "baseAsset": "KSM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "566.46289854", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KSMBTC" + }, + { + "baseAsset": "KSM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "576.25906740", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KSMBUSD" + }, + { + "baseAsset": "KSM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1797.90709318", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KSMUSDT" + }, + { + "baseAsset": "EGLD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "504.47724113", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EGLDBNB" + }, + { + "baseAsset": "EGLD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "932.52390548", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EGLDBTC" + }, + { + "baseAsset": "EGLD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1930.04876997", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EGLDBUSD" + }, + { + "baseAsset": "EGLD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4687.18435719", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EGLDUSDT" + }, + { + "baseAsset": "DIA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10783.91028205", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DIABNB" + }, + { + "baseAsset": "DIA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "56725.35330090", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DIABTC" + }, + { + "baseAsset": "DIA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "68267.03592772", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DIABUSD" + }, + { + "baseAsset": "DIA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "210718.73210562", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DIAUSDT" + }, + { + "baseAsset": "RUNE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "54531.67380125", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RUNEUSDT" + }, + { + "baseAsset": "FIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "644225.60375260", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FIOUSDT" + }, + { + "baseAsset": "UMA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5336.88492008", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UMABTC" + }, + { + "baseAsset": "UMA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "17049.64259902", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UMAUSDT" + }, + { + "baseAsset": "EOSUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.40210000", + "minPrice": "0.02120000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "877225.37300000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "EOSUPUSDT" + }, + { + "baseAsset": "EOSDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.00071410", + "minPrice": "0.00003760", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "69980060.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "46275837.98623610", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "EOSDOWNUSDT" + }, + { + "baseAsset": "TRXUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.13072000", + "minPrice": "0.00689000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "72727.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRXUPUSDT" + }, + { + "baseAsset": "TRXDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "20.60900000", + "minPrice": "1.08500000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "89984117.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1037.13000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "460.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRXDOWNUSDT" + }, + { + "baseAsset": "XRPUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.86630000", + "minPrice": "0.09830000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "289380.11523280", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "5096.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XRPUPUSDT" + }, + { + "baseAsset": "XRPDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.00155620", + "minPrice": "0.00008200", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "99999999.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "40086367.39908269", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "6124449.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XRPDOWNUSDT" + }, + { + "baseAsset": "DOTUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "2.11410000", + "minPrice": "0.11130000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "227630.89259207", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "4492.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTUPUSDT" + }, + { + "baseAsset": "DOTDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "47.99800000", + "minPrice": "2.52700000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2480.41462821", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "198.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTDOWNUSDT" + }, + { + "baseAsset": "SRM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "50000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6857.90236667", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "BREAK", + "symbol": "SRMBIDR" + }, + { + "baseAsset": "ONE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00", + "minPrice": "0.01", + "tickSize": "0.01" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "346216.39305556", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "BREAK", + "symbol": "ONEBIDR" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2131.75715774", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKTRY" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1700.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922320.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "88751.29548297", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "USDTNGN" + }, + { + "baseAsset": "BEL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "30812.83835997", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BELBNB" + }, + { + "baseAsset": "BEL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "25269.83446838", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BELBTC" + }, + { + "baseAsset": "BEL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16160.25517720", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BELBUSD" + }, + { + "baseAsset": "BEL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "77345.69457956", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BELUSDT" + }, + { + "baseAsset": "WING", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1395.81408333", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "WINGBNB" + }, + { + "baseAsset": "WING", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4170.05271021", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WINGBTC" + }, + { + "baseAsset": "SWRV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "41836.63055555", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SWRVBNB" + }, + { + "baseAsset": "SWRV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "134196.55689027", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SWRVBUSD" + }, + { + "baseAsset": "WING", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1828.68230020", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WINGBUSD" + }, + { + "baseAsset": "WING", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6150.04052119", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WINGUSDT" + }, + { + "baseAsset": "LTCUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.92300000", + "minPrice": "0.04860000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "343363.27451282", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LTCUPUSDT" + }, + { + "baseAsset": "LTCDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "4.23210000", + "minPrice": "0.22280000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "610819340.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4729.95414102", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LTCDOWNUSDT" + }, + { + "baseAsset": "LEND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000007.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92232.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92232.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LENDBKRW" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9824.81403752", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPEUR" + }, + { + "baseAsset": "CREAM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "838.02263933", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CREAMBNB" + }, + { + "baseAsset": "CREAM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2466.51532800", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CREAMBUSD" + }, + { + "baseAsset": "UNI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7384.65927727", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNIBNB" + }, + { + "baseAsset": "UNI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "22452.74454482", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNIBTC" + }, + { + "baseAsset": "UNI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "22095.71360111", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNIBUSD" + }, + { + "baseAsset": "UNI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "69234.90850590", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNIUSDT" + }, + { + "baseAsset": "NBS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "26481257.80231065", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "NBSBTC" + }, + { + "baseAsset": "NBS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4983696.01369006", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NBSUSDT" + }, + { + "baseAsset": "OXT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "257146.69284225", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OXTBTC" + }, + { + "baseAsset": "OXT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "417458.22523974", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OXTUSDT" + }, + { + "baseAsset": "SUN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "10000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "952.42584444", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SUNBTC" + }, + { + "baseAsset": "SUN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8415536.88672689", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUNUSDT" + }, + { + "baseAsset": "AVAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2283.95183460", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAXBNB" + }, + { + "baseAsset": "AVAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7106.03205003", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAXBTC" + }, + { + "baseAsset": "AVAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11917.65303683", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAXBUSD" + }, + { + "baseAsset": "AVAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "45278.87678943", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAXUSDT" + }, + { + "baseAsset": "HNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3277.58662960", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HNTBTC" + }, + { + "baseAsset": "HNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15532.11193189", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HNTUSDT" + }, + { + "baseAsset": "BAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "66548.83203613", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BAKEBNB" + }, + { + "baseAsset": "BURGER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16266.16066712", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BURGERBNB" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3610.02439193", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "SXPBIDR" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9200.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "709.81472222", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LINKBKRW" + }, + { + "baseAsset": "FLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "24574.78335645", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "FLMBNB" + }, + { + "baseAsset": "FLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "108422.45448227", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FLMBTC" + }, + { + "baseAsset": "FLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "66549.65670604", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FLMBUSD" + }, + { + "baseAsset": "FLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "369534.84155663", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FLMUSDT" + }, + { + "baseAsset": "SCRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "37517.86747741", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SCRTBTC" + }, + { + "baseAsset": "SCRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "31069.57053509", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SCRTETH" + }, + { + "baseAsset": "CAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16034.55505211", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CAKEBNB" + }, + { + "baseAsset": "CAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "19769.18659555", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CAKEBUSD" + }, + { + "baseAsset": "SPARTA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "464747.21195274", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SPARTABNB" + }, + { + "baseAsset": "UNIUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "2.01800000", + "minPrice": "0.10700000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "30991.30402777", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "UNIUPUSDT" + }, + { + "baseAsset": "UNIDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "4.15000000", + "minPrice": "0.21900000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "99999999.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9458.34577777", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "2302.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "UNIDOWNUSDT" + }, + { + "baseAsset": "ORN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7294.78220986", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ORNBTC" + }, + { + "baseAsset": "ORN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21935.50466990", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ORNUSDT" + }, + { + "baseAsset": "TRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922320.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "84152.61594126", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "TRXNGN" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "38583.25114662", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPTRY" + }, + { + "baseAsset": "UTK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "283424.91104933", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UTKBTC" + }, + { + "baseAsset": "UTK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "563085.60498957", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UTKUSDT" + }, + { + "baseAsset": "XVS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2468.09575399", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XVSBNB" + }, + { + "baseAsset": "XVS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4668.80138985", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XVSBTC" + }, + { + "baseAsset": "XVS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3100.22770674", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XVSBUSD" + }, + { + "baseAsset": "XVS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16226.13066712", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XVSUSDT" + }, + { + "baseAsset": "ALPHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "99135.51772063", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALPHABNB" + }, + { + "baseAsset": "ALPHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "101165.33009034", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALPHABTC" + }, + { + "baseAsset": "ALPHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "87361.49409312", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALPHABUSD" + }, + { + "baseAsset": "ALPHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "223085.28422515", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALPHAUSDT" + }, + { + "baseAsset": "VIDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "140492.64489228", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VIDTBTC" + }, + { + "baseAsset": "VIDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "40751.56219596", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VIDTBUSD" + }, + { + "baseAsset": "AAVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "323.80820291", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AAVEBNB" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11.93861382", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCBRL" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "7.95900000", + "minPrice": "2.65300000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "941758.37587213", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "USDTBRL" + }, + { + "baseAsset": "AAVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1496.25022932", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AAVEBTC" + }, + { + "baseAsset": "AAVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "529.61993676", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AAVEETH" + }, + { + "baseAsset": "AAVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1351.41688658", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AAVEBUSD" + }, + { + "baseAsset": "AAVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3129.78885337", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AAVEUSDT" + }, + { + "baseAsset": "AAVE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "5000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "129.78107222", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "AAVEBKRW" + }, + { + "baseAsset": "NEAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6960.55955524", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NEARBNB" + }, + { + "baseAsset": "NEAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "23845.44419735", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NEARBTC" + }, + { + "baseAsset": "NEAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "29075.31521890", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NEARBUSD" + }, + { + "baseAsset": "NEAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "112774.23307852", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NEARUSDT" + }, + { + "baseAsset": "SXPUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.33141000", + "minPrice": "0.01745000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "90765.55309722", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SXPUPUSDT" + }, + { + "baseAsset": "SXPDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.17035000", + "minPrice": "0.00897000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "109747.72490277", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SXPDOWNUSDT" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9200.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1469.73875000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BKRW", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DOTBKRW" + }, + { + "baseAsset": "SXP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1807.67435719", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SXPGBP" + }, + { + "baseAsset": "FIL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5227.09601111", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FILBNB" + }, + { + "baseAsset": "FIL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12862.32597637", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FILBTC" + }, + { + "baseAsset": "FIL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8039.86289784", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FILBUSD" + }, + { + "baseAsset": "FIL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "30665.01701876", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FILUSDT" + }, + { + "baseAsset": "FILUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "FILUPUSDT" + }, + { + "baseAsset": "FILDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "4611.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "19998638.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "19998638.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "FILDOWNUSDT" + }, + { + "baseAsset": "YFIUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.22040000", + "minPrice": "0.01170000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "YFIUPUSDT" + }, + { + "baseAsset": "YFIDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.74790000", + "minPrice": "0.09200000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "30000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7576.60135546", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_POSITION", + "maxPosition": "2775.00000000" + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "YFIDOWNUSDT" + }, + { + "baseAsset": "INJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6418.81327310", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "INJBNB" + }, + { + "baseAsset": "INJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10001.20535093", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "INJBTC" + }, + { + "baseAsset": "INJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9549.73245309", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "INJBUSD" + }, + { + "baseAsset": "INJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "32401.48373870", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "INJUSDT" + }, + { + "baseAsset": "AERGO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "158553.92703266", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AERGOBTC" + }, + { + "baseAsset": "AERGO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "108971.38776928", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AERGOBUSD" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6102.32134815", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKEUR" + }, + { + "baseAsset": "ONE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1202865.51876302", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ONEBUSD" + }, + { + "baseAsset": "EASY", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2628.06282835", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "EASYETH" + }, + { + "baseAsset": "AUDIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "65589.26275191", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUDIOBTC" + }, + { + "baseAsset": "AUDIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "65174.18811674", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUDIOBUSD" + }, + { + "baseAsset": "AUDIO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "185144.52731063", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUDIOUSDT" + }, + { + "baseAsset": "CTK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "18525.03551077", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTKBNB" + }, + { + "baseAsset": "CTK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "25600.56400277", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTKBTC" + }, + { + "baseAsset": "CTK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "20504.05621959", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTKBUSD" + }, + { + "baseAsset": "CTK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "87957.99485753", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CTKUSDT" + }, + { + "baseAsset": "BCHUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BCHUPUSDT" + }, + { + "baseAsset": "BCHDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "896492.23471794", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BCHDOWNUSDT" + }, + { + "baseAsset": "BOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "29.90542958", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BOTBTC" + }, + { + "baseAsset": "BOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "26.01151619", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BOTBUSD" + }, + { + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "200000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "45000.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "72.95349328", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHBRL" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8012.65950034", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTEUR" + }, + { + "baseAsset": "AKRO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "23017404.62682418", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AKROBTC" + }, + { + "baseAsset": "AKRO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4124642.99791521", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AKROUSDT" + }, + { + "baseAsset": "KP3R", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "54.41614315", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KP3RBNB" + }, + { + "baseAsset": "KP3R", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "101.33801945", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "KP3RBUSD" + }, + { + "baseAsset": "AXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1719.36220291", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AXSBNB" + }, + { + "baseAsset": "AXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5532.26384989", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AXSBTC" + }, + { + "baseAsset": "AXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6626.41348158", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AXSBUSD" + }, + { + "baseAsset": "AXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11928.10900625", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AXSUSDT" + }, + { + "baseAsset": "HARD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "36811.70326615", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HARDBNB" + }, + { + "baseAsset": "HARD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "73851.82800555", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HARDBTC" + }, + { + "baseAsset": "HARD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "55285.56219596", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HARDBUSD" + }, + { + "baseAsset": "HARD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "147038.58609451", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HARDUSDT" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "45000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "250.68117765", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBBRL" + }, + { + "baseAsset": "LTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1113.27009409", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LTCEUR" + }, + { + "baseAsset": "RENBTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1.33818897", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RENBTCBTC" + }, + { + "baseAsset": "RENBTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "0.21179162", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "RENBTCETH" + }, + { + "baseAsset": "DNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "189128.84864489", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DNTBUSD" + }, + { + "baseAsset": "DNT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "750323.92619874", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DNTUSDT" + }, + { + "baseAsset": "SLP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8141059.50104239", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SLPETH" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "311287.71894371", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADAEUR" + }, + { + "baseAsset": "LTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10001487.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9221.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "34.73269770", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LTCNGN" + }, + { + "baseAsset": "CVP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "28530.07435719", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CVPETH" + }, + { + "baseAsset": "CVP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "38056.61855455", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CVPBUSD" + }, + { + "baseAsset": "STRAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "29457.64371091", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STRAXBTC" + }, + { + "baseAsset": "STRAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "28088.03036831", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STRAXETH" + }, + { + "baseAsset": "STRAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15582.50159833", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STRAXBUSD" + }, + { + "baseAsset": "STRAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "57176.71289784", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "STRAXUSDT" + }, + { + "baseAsset": "FOR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1471929.50312717", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FORBTC" + }, + { + "baseAsset": "FOR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "504102.88408617", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FORBUSD" + }, + { + "baseAsset": "UNFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3180.18055555", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "UNFIBNB" + }, + { + "baseAsset": "UNFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7102.81785962", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNFIBTC" + }, + { + "baseAsset": "UNFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3757.75399583", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNFIBUSD" + }, + { + "baseAsset": "UNFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "22887.18365184", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNFIUSDT" + }, + { + "baseAsset": "FRONT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21260.15384615", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "FRONTETH" + }, + { + "baseAsset": "FRONT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "102306.93745656", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FRONTBUSD" + }, + { + "baseAsset": "BCHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3892.83844544", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BCHABUSD" + }, + { + "baseAsset": "ROSE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "978529.50660180", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ROSEBTC" + }, + { + "baseAsset": "ROSE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1105962.99652536", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ROSEBUSD" + }, + { + "baseAsset": "ROSE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2721384.49777623", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ROSEUSDT" + }, + { + "baseAsset": "AVAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4356.01185545", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAXTRY" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "7.95700000", + "minPrice": "2.65200000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "519767.99699791", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BUSDBRL" + }, + { + "baseAsset": "AVA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "82304.91690757", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAUSDT" + }, + { + "baseAsset": "SYS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "171734.76997915", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SYSBUSD" + }, + { + "baseAsset": "XEM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1336550.45872133", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XEMUSDT" + }, + { + "baseAsset": "HEGIC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "304146.99166666", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "HEGICETH" + }, + { + "baseAsset": "HEGIC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "678358.80889506", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HEGICBUSD" + }, + { + "baseAsset": "AAVEUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.13320000", + "minPrice": "0.05970000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "309763.94887500", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "AAVEUPUSDT" + }, + { + "baseAsset": "AAVEDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.00893400", + "minPrice": "0.00047100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2535602.50552777", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "AAVEDOWNUSDT" + }, + { + "baseAsset": "PROM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3352.65007644", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PROMBNB" + }, + { + "baseAsset": "PROM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3625.32927727", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PROMBUSD" + }, + { + "baseAsset": "XRP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "66045.28104239", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XRPBRL" + }, + { + "baseAsset": "XRP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922320.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "56903.77744993", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XRPNGN" + }, + { + "baseAsset": "SKL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "419147.28075052", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SKLBTC" + }, + { + "baseAsset": "SKL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "291750.14037526", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SKLBUSD" + }, + { + "baseAsset": "SKL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1423571.54621264", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SKLUSDT" + }, + { + "baseAsset": "BCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "174.23370118", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BCHEUR" + }, + { + "baseAsset": "YFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1.71943624", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YFIEUR" + }, + { + "baseAsset": "ZIL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "557795.13134120", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "ZILBIDR" + }, + { + "baseAsset": "SUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "67502.78819444", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SUSDBTC" + }, + { + "baseAsset": "SUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "87734.33472222", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SUSDETH" + }, + { + "baseAsset": "SUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "171281.34600416", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUSDUSDT" + }, + { + "baseAsset": "COVER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "125.88983333", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "COVERETH" + }, + { + "baseAsset": "COVER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1123.92356282", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "COVERBUSD" + }, + { + "baseAsset": "GLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "111886.01111883", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GLMBTC" + }, + { + "baseAsset": "GLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "135643.76372480", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GLMETH" + }, + { + "baseAsset": "GHST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12027.28339124", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GHSTETH" + }, + { + "baseAsset": "GHST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "19096.83405142", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GHSTBUSD" + }, + { + "baseAsset": "SUSHIUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.00892200", + "minPrice": "0.00047000", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "150000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "150000.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SUSHIUPUSDT" + }, + { + "baseAsset": "SUSHIDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "71.80200000", + "minPrice": "3.78000000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "19998638.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "559.52870512", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SUSHIDOWNUSDT" + }, + { + "baseAsset": "XLMUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.04624000", + "minPrice": "0.00244000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "920000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XLMUPUSDT" + }, + { + "baseAsset": "XLMDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "8.18200000", + "minPrice": "0.43100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "399280174.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1112.05837500", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "XLMDOWNUSDT" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "45000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2048.31382465", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKBRL" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000007.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92232.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "194.67417470", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "LINKNGN" + }, + { + "baseAsset": "LTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "222.83302546", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "RUB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LTCRUB" + }, + { + "baseAsset": "TRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "463192.68519805", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRXTRY" + }, + { + "baseAsset": "XLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "434450.22772758", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XLMEUR" + }, + { + "baseAsset": "DF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "161696.54428754", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DFETH" + }, + { + "baseAsset": "DF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "327300.65155663", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DFBUSD" + }, + { + "baseAsset": "GRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "259914.44127866", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GRTBTC" + }, + { + "baseAsset": "GRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "108087.00694927", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GRTETH" + }, + { + "baseAsset": "GRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1062021.57609451", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GRTUSDT" + }, + { + "baseAsset": "JUV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2727.75831827", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "JUVBTC" + }, + { + "baseAsset": "JUV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2570.61057678", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "JUVBUSD" + }, + { + "baseAsset": "JUV", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9907.24391243", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "JUVUSDT" + }, + { + "baseAsset": "PSG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2202.00474635", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PSGBTC" + }, + { + "baseAsset": "PSG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1878.75176511", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PSGBUSD" + }, + { + "baseAsset": "PSG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6162.32806810", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PSGUSDT" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "35943.00", + "minPrice": "11981.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "30000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "87611.87435897", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BVND", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "BREAK", + "symbol": "BUSDBVND" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "36010.00", + "minPrice": "12003.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "1000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "30000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "93325.52717948", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BVND", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "BREAK", + "symbol": "USDTBVND" + }, + { + "baseAsset": "1INCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "64265.33829047", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "1INCHBTC" + }, + { + "baseAsset": "1INCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "117413.62025712", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "1INCHUSDT" + }, + { + "baseAsset": "REEF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "REEFBTC" + }, + { + "baseAsset": "REEF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10399283.35719249", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "REEFUSDT" + }, + { + "baseAsset": "OG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "6422.76908964", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OGBTC" + }, + { + "baseAsset": "OG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "18346.32856150", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OGUSDT" + }, + { + "baseAsset": "ATM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4526.67984016", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ATMBTC" + }, + { + "baseAsset": "ATM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10976.30746977", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ATMUSDT" + }, + { + "baseAsset": "ASR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5866.91049339", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ASRBTC" + }, + { + "baseAsset": "ASR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "18610.76685198", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ASRUSDT" + }, + { + "baseAsset": "CELO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "32305.37060458", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CELOBTC" + }, + { + "baseAsset": "CELO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "113775.51887421", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CELOUSDT" + }, + { + "baseAsset": "RIF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "377740.92147324", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RIFBTC" + }, + { + "baseAsset": "RIF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "408622.09937456", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RIFUSDT" + }, + { + "baseAsset": "CHZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "341941.69284225", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHZTRY" + }, + { + "baseAsset": "XLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "112937.90560111", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XLMTRY" + }, + { + "baseAsset": "LINK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3979.94578735", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINKGBP" + }, + { + "baseAsset": "GRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "116878.07788742", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GRTEUR" + }, + { + "baseAsset": "BTCST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2265.90039610", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCSTBTC" + }, + { + "baseAsset": "BTCST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1220.09589298", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCSTBUSD" + }, + { + "baseAsset": "BTCST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5357.73226546", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTCSTUSDT" + }, + { + "baseAsset": "TRU", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "479372.90472550", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRUBTC" + }, + { + "baseAsset": "TRU", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "461741.85729139", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "TRUBUSD" + }, + { + "baseAsset": "TRU", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "949208.36890896", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRUUSDT" + }, + { + "baseAsset": "DEXE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4198.42800555", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DEXEETH" + }, + { + "baseAsset": "DEXE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4005.48746351", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DEXEBUSD" + }, + { + "baseAsset": "EOS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "24164.45140375", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EOSEUR" + }, + { + "baseAsset": "LTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "45000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "258.87431805", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LTCBRL" + }, + { + "baseAsset": "USDC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.30000000", + "minPrice": "0.70000000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15096754.13355803", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "USDCBUSD" + }, + { + "baseAsset": "TUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.29980000", + "minPrice": "0.69990000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1419966.43222376", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TUSDBUSD" + }, + { + "baseAsset": "PAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.29980000", + "minPrice": "0.69990000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1444297.23345314", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "PAXBUSD" + }, + { + "baseAsset": "CKB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "47930690.92911744", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CKBBTC" + }, + { + "baseAsset": "CKB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2966829.69979152", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CKBBUSD" + }, + { + "baseAsset": "CKB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7571524.91799861", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CKBUSDT" + }, + { + "baseAsset": "TWT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "63884.03891591", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TWTBTC" + }, + { + "baseAsset": "TWT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "110258.07766504", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TWTBUSD" + }, + { + "baseAsset": "TWT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "350857.00347463", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TWTUSDT" + }, + { + "baseAsset": "FIRO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4598.18270326", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FIROBTC" + }, + { + "baseAsset": "FIRO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4548.83889602", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "FIROETH" + }, + { + "baseAsset": "FIRO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15083.86778318", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FIROUSDT" + }, + { + "baseAsset": "BETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12397.15035872", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BETHETH" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "986692.35052119", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGEEUR" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "890698.22654621", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGETRY" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "350599.17053509", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGEAUD" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "296409.70182765", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGEBRL" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000007.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92232.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "500.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "57.57163053", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "NGN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "DOTNGN" + }, + { + "baseAsset": "PROS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "52447.50868658", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PROSETH" + }, + { + "baseAsset": "LIT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "20051.13606671", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LITBTC" + }, + { + "baseAsset": "LIT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12499.85156358", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LITBUSD" + }, + { + "baseAsset": "LIT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "60679.82640722", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LITUSDT" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "0.29455219", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "VAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTCVAI" + }, + { + "baseAsset": "BUSD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "2.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.8", + "multiplierUp": "1.2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "138342.26754690", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "VAI", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BUSDVAI" + }, + { + "baseAsset": "SFP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "30541.42599027", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SFPBTC" + }, + { + "baseAsset": "SFP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "66908.01968033", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SFPBUSD" + }, + { + "baseAsset": "SFP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "107879.29603891", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SFPUSDT" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "375368.28617095", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGEGBP" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2288.85010423", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTTRY" + }, + { + "baseAsset": "FXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2088.37338429", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FXSBTC" + }, + { + "baseAsset": "FXS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2259.73544127", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FXSBUSD" + }, + { + "baseAsset": "DODO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "34324.68054204", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DODOBTC" + }, + { + "baseAsset": "DODO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "62265.18978457", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DODOBUSD" + }, + { + "baseAsset": "DODO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "193621.43349548", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DODOUSDT" + }, + { + "baseAsset": "FRONT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92245.03474635", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FRONTBTC" + }, + { + "baseAsset": "EASY", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4804.61032662", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "EASYBTC" + }, + { + "baseAsset": "CAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "38162.64923558", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CAKEBTC" + }, + { + "baseAsset": "CAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "42655.41237665", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CAKEUSDT" + }, + { + "baseAsset": "BAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "39417.77282835", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BAKEBUSD" + }, + { + "baseAsset": "UFT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "140468.81931897", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UFTETH" + }, + { + "baseAsset": "UFT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "78327.74913134", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UFTBUSD" + }, + { + "baseAsset": "1INCH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "42738.61528839", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "1INCHBUSD" + }, + { + "baseAsset": "BAND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10119.52133425", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BANDBUSD" + }, + { + "baseAsset": "GRT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "292569.56090340", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "GRTBUSD" + }, + { + "baseAsset": "IOST", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2872495.71507991", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "IOSTBUSD" + }, + { + "baseAsset": "OMG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "18517.52821403", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OMGBUSD" + }, + { + "baseAsset": "REEF", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2695919.80319666", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "REEFBUSD" + }, + { + "baseAsset": "ACM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5042.47755385", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ACMBTC" + }, + { + "baseAsset": "ACM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3890.65823488", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ACMBUSD" + }, + { + "baseAsset": "ACM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12026.90500347", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ACMUSDT" + }, + { + "baseAsset": "AUCTION", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4041.94968728", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUCTIONBTC" + }, + { + "baseAsset": "AUCTION", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2741.01019457", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUCTIONBUSD" + }, + { + "baseAsset": "PHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "160707.94996525", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PHABTC" + }, + { + "baseAsset": "PHA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "132984.18311327", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PHABUSD" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3739.89892147", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTGBP" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "84842.48540653", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADATRY" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "60527.99993050", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADABRL" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "104289.84229325", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADAGBP" + }, + { + "baseAsset": "TVK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "328104.80889506", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TVKBTC" + }, + { + "baseAsset": "TVK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "304212.70187630", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TVKBUSD" + }, + { + "baseAsset": "BADGER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2828.83677553", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BADGERBTC" + }, + { + "baseAsset": "BADGER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1283.03624044", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BADGERBUSD" + }, + { + "baseAsset": "BADGER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10400.49273731", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BADGERUSDT" + }, + { + "baseAsset": "FIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "33933.20145934", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FISBTC" + }, + { + "baseAsset": "FIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "22004.47949965", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FISBUSD" + }, + { + "baseAsset": "FIS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "109199.20217512", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FISUSDT" + }, + { + "baseAsset": "DOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2495.99419735", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOTBRL" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "87373.93161917", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADAAUD" + }, + { + "baseAsset": "HOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15009195.30090340", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HOTTRY" + }, + { + "baseAsset": "EGLD", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "672.48075747", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EGLDEUR" + }, + { + "baseAsset": "OM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "379411.06671299", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OMBTC" + }, + { + "baseAsset": "OM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "279780.48088950", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OMBUSD" + }, + { + "baseAsset": "OM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "743465.35583738", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "OMUSDT" + }, + { + "baseAsset": "POND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3066648.56914523", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PONDBTC" + }, + { + "baseAsset": "POND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1929439.07284225", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PONDBUSD" + }, + { + "baseAsset": "POND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4268861.51322446", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PONDUSDT" + }, + { + "baseAsset": "DEGO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5074.29699096", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DEGOBTC" + }, + { + "baseAsset": "DEGO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4110.71256428", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DEGOBUSD" + }, + { + "baseAsset": "DEGO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "20527.66777484", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DEGOUSDT" + }, + { + "baseAsset": "AVAX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2380.76403057", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AVAXEUR" + }, + { + "baseAsset": "BTT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16639729.12179487", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTTTRY" + }, + { + "baseAsset": "CHZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "167820.68637943", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHZBRL" + }, + { + "baseAsset": "UNI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4148.38042529", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "UNIEUR" + }, + { + "baseAsset": "ALICE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "15519.84492008", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALICEBTC" + }, + { + "baseAsset": "ALICE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16830.38906879", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALICEBUSD" + }, + { + "baseAsset": "ALICE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "83217.56795691", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALICEUSDT" + }, + { + "baseAsset": "CHZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "633500.59444058", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHZBUSD" + }, + { + "baseAsset": "CHZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "289361.30489228", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHZEUR" + }, + { + "baseAsset": "CHZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "148309.13148019", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CHZGBP" + }, + { + "baseAsset": "BIFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21.69697361", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BIFIBNB" + }, + { + "baseAsset": "BIFI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21.16575191", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BIFIBUSD" + }, + { + "baseAsset": "LINA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12108405.93815149", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINABTC" + }, + { + "baseAsset": "LINA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1037256.91626824", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINABUSD" + }, + { + "baseAsset": "LINA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5458598.74815844", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LINAUSDT" + }, + { + "baseAsset": "ADA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "23125.29710910", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "RUB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ADARUB" + }, + { + "baseAsset": "ENJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "17195.58088950", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ENJBRL" + }, + { + "baseAsset": "ENJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "36105.09173036", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ENJEUR" + }, + { + "baseAsset": "MATIC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "113079.81883252", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MATICEUR" + }, + { + "baseAsset": "NEO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1060.95116052", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "NEOTRY" + }, + { + "baseAsset": "PERP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5040.30342599", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PERPBTC" + }, + { + "baseAsset": "PERP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4955.93517025", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PERPBUSD" + }, + { + "baseAsset": "PERP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "21210.76700764", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PERPUSDT" + }, + { + "baseAsset": "RAMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "216993.07366226", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RAMPBTC" + }, + { + "baseAsset": "RAMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "138615.50660180", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RAMPBUSD" + }, + { + "baseAsset": "RAMP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "959126.14840166", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RAMPUSDT" + }, + { + "baseAsset": "SUPER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "91011.39541348", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUPERBTC" + }, + { + "baseAsset": "SUPER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "57436.60932592", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUPERBUSD" + }, + { + "baseAsset": "SUPER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "264322.93954134", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SUPERUSDT" + }, + { + "baseAsset": "CFX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "204530.29812369", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CFXBTC" + }, + { + "baseAsset": "CFX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "194135.30507296", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CFXBUSD" + }, + { + "baseAsset": "CFX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1113770.04586518", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CFXUSDT" + }, + { + "baseAsset": "ENJ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "20873.55712300", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ENJGBP" + }, + { + "baseAsset": "EOS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10868.00472550", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EOSTRY" + }, + { + "baseAsset": "LTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "399.44987769", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LTCGBP" + }, + { + "baseAsset": "LUNA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5307.79311327", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LUNAEUR" + }, + { + "baseAsset": "RVN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "497756.54566365", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RVNTRY" + }, + { + "baseAsset": "THETA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "12297.85934676", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "THETAEUR" + }, + { + "baseAsset": "XVG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2046528.41195274", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XVGBUSD" + }, + { + "baseAsset": "EPS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "140400.97011813", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EPSBTC" + }, + { + "baseAsset": "EPS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "68685.95066018", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EPSBUSD" + }, + { + "baseAsset": "EPS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "450454.22376650", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EPSUSDT" + }, + { + "baseAsset": "AUTO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "49.19878179", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUTOBTC" + }, + { + "baseAsset": "AUTO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "25.54648783", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUTOBUSD" + }, + { + "baseAsset": "AUTO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "151.77734885", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "AUTOUSDT" + }, + { + "baseAsset": "TKO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "28861.76719944", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TKOBTC" + }, + { + "baseAsset": "TKO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00", + "minPrice": "0.01", + "tickSize": "0.01" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "97157.93071577", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "TKOBIDR" + }, + { + "baseAsset": "TKO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "25447.74184850", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TKOBUSD" + }, + { + "baseAsset": "TKO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "115900.54052814", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TKOUSDT" + }, + { + "baseAsset": "PUNDIX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "34327.78207088", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PUNDIXETH" + }, + { + "baseAsset": "PUNDIX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "90823.32883947", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "PUNDIXUSDT" + }, + { + "baseAsset": "BTT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9602781.02461538", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTTBRL" + }, + { + "baseAsset": "BTT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11203027.12948717", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTTEUR" + }, + { + "baseAsset": "HOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5994661.03293954", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HOTEUR" + }, + { + "baseAsset": "WIN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "913205152.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "82038181.98262682", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WINEUR" + }, + { + "baseAsset": "TLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "799224.72077831", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TLMBTC" + }, + { + "baseAsset": "TLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "648365.49719249", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TLMBUSD" + }, + { + "baseAsset": "TLM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "900000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2888901.69214732", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TLMUSDT" + }, + { + "baseAsset": "1INCHUP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.12025500", + "minPrice": "0.00633000", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "300000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "300000.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "1INCHUPUSDT" + }, + { + "baseAsset": "1INCHDOWN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "0.01190300", + "minPrice": "0.00062700", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "2999958.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2999958.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "LEVERAGED" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": false, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "1INCHDOWNUSDT" + }, + { + "baseAsset": "BTG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "236.22647116", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTGBUSD" + }, + { + "baseAsset": "BTG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1958.82070187", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BTGUSDT" + }, + { + "baseAsset": "HOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11098996.99381514", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "HOTBUSD" + }, + { + "baseAsset": "BNB", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000000.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "69.31744892", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "UAH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BNBUAH" + }, + { + "baseAsset": "ONT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "37050.24547602", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ONTTRY" + }, + { + "baseAsset": "VET", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1858453.29257123", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VETEUR" + }, + { + "baseAsset": "VET", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "708038.94976372", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VETGBP" + }, + { + "baseAsset": "WIN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "913205152.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "54981325.61084086", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WINBRL" + }, + { + "baseAsset": "MIR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "24612.38651841", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MIRBTC" + }, + { + "baseAsset": "MIR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "26065.50194579", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MIRBUSD" + }, + { + "baseAsset": "MIR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "96464.51091035", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MIRUSDT" + }, + { + "baseAsset": "BAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4120.53965948", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BARBTC" + }, + { + "baseAsset": "BAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1772.08528144", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BARBUSD" + }, + { + "baseAsset": "BAR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9051.71223071", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BARUSDT" + }, + { + "baseAsset": "FORTH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2223.41934676", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FORTHBTC" + }, + { + "baseAsset": "FORTH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3659.41653926", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FORTHBUSD" + }, + { + "baseAsset": "FORTH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "17852.81134815", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "FORTHUSDT" + }, + { + "baseAsset": "CAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2761.64315496", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "CAKEGBP" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "202454.93606671", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "RUB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "DOGERUB" + }, + { + "baseAsset": "HOT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3266631.29871794", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "HOTBRL" + }, + { + "baseAsset": "WRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "25174.41084086", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WRXEUR" + }, + { + "baseAsset": "EZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10584.51377345", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EZBTC" + }, + { + "baseAsset": "EZ", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "13360.77963863", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "EZETH" + }, + { + "baseAsset": "BAKE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "161023.39735927", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BAKEUSDT" + }, + { + "baseAsset": "BURGER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "7630.42696316", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BURGERBUSD" + }, + { + "baseAsset": "BURGER", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "44449.26997915", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BURGERUSDT" + }, + { + "baseAsset": "SLP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "29414618.96803335", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SLPBUSD" + }, + { + "baseAsset": "SLP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SLPUSDT" + }, + { + "baseAsset": "TRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "710466.25512820", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "AUD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "TRXAUD" + }, + { + "baseAsset": "TRX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "90000000.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "730581.55663655", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TRXEUR" + }, + { + "baseAsset": "VET", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "482192.94065323", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "VETTRY" + }, + { + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SHIBUSDT" + }, + { + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "42896390116.09", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SHIBBUSD" + }, + { + "baseAsset": "ICP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "11106.63924947", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ICPBTC" + }, + { + "baseAsset": "ICP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5442.38671299", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ICPBNB" + }, + { + "baseAsset": "ICP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16770.52536483", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ICPBUSD" + }, + { + "baseAsset": "ICP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "49902.55734537", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ICPUSDT" + }, + { + "baseAsset": "BTC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "12000000.00000000", + "minPrice": "5500000.00000000", + "tickSize": "1.00000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "153.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GYEN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "BTCGYEN" + }, + { + "baseAsset": "USDT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "120.00000000", + "minPrice": "90.00000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "1000.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GYEN", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "USDTGYEN" + }, + { + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "8758184122.87", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SHIBEUR" + }, + { + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "100.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2631653832.05", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "RUB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "SHIBRUB" + }, + { + "baseAsset": "ETC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1062.75084086", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "EUR", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETCEUR" + }, + { + "baseAsset": "ETC", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "555.99688461", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "ETCBRL" + }, + { + "baseAsset": "DOGE", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00", + "minPrice": "1.00", + "tickSize": "1.00" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "20000.00" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "340281.32522585", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 2, + "status": "TRADING", + "symbol": "DOGEBIDR" + }, + { + "baseAsset": "AR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "3222.85722724", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ARBTC" + }, + { + "baseAsset": "AR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1117.41462126", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ARBNB" + }, + { + "baseAsset": "AR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2824.80125086", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ARBUSD" + }, + { + "baseAsset": "AR", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "100000.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "13414.11299513", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ARUSDT" + }, + { + "baseAsset": "POLS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "29369.57357887", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "POLSBTC" + }, + { + "baseAsset": "POLS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "22429.22703266", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "POLSBNB" + }, + { + "baseAsset": "POLS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "20252.01702571", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "POLSBUSD" + }, + { + "baseAsset": "POLS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "73550.38264072", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "POLSUSDT" + }, + { + "baseAsset": "MDX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "112287.78123697", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MDXBTC" + }, + { + "baseAsset": "MDX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "64583.43363286", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "MDXBNB" + }, + { + "baseAsset": "MDX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "88244.30743571", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MDXBUSD" + }, + { + "baseAsset": "MDX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "589168.70326615", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MDXUSDT" + }, + { + "baseAsset": "MASK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5721.16507296", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MASKBNB" + }, + { + "baseAsset": "MASK", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "10802.06583738", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BNBBEARUSDT" + "status": "TRADING", + "symbol": "MASKBUSD" }, { - "baseAsset": "BNBBEAR", + "baseAsset": "MASK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -73062,9 +120937,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -73078,7 +120953,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2323.69823608", + "maxQty": "33051.24801945", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73092,7 +120967,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -73103,26 +120978,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BNBBEARBUSD" + "status": "TRADING", + "symbol": "MASKUSDT" }, { - "baseAsset": "STPT", + "baseAsset": "LPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -73132,15 +121008,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141570.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -73148,7 +121024,78 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "389641.21603261", + "maxQty": "2140.45292564", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "LPTBTC" + }, + { + "baseAsset": "LPT", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141570.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1150.21759555", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73180,19 +121127,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "STPTBNB" + "status": "TRADING", + "symbol": "LPTBNB" }, { - "baseAsset": "STPT", + "baseAsset": "LPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -73202,15 +121149,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222440.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -73218,7 +121165,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3959363.40069444", + "maxQty": "1850.19990271", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73246,24 +121193,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STPTBTC" + "symbol": "LPTBUSD" }, { - "baseAsset": "STPT", + "baseAsset": "LPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -73273,9 +121220,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222440.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -73289,7 +121236,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1285465.16951389", + "maxQty": "5496.78339819", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73323,16 +121270,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STPTUSDT" + "symbol": "LPTUSDT" }, { - "baseAsset": "BTC", + "baseAsset": "ETH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", + "maxPrice": "9999319.00000000", "minPrice": "1.00000000", "tickSize": "1.00000000" }, @@ -73344,9 +121291,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9220.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -73360,7 +121307,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "0.69717767", + "maxQty": "22.52301181", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73387,24 +121334,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ZAR", + "quoteAsset": "UAH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCZAR" + "symbol": "ETHUAH" }, { - "baseAsset": "ETH", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.10000000", - "tickSize": "0.10000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -73414,15 +121361,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222440.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -73430,7 +121377,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61.04912299", + "maxQty": "27743.95580264", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73457,22 +121404,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ZAR", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHZAR" + "symbol": "MATICBRL" }, { - "baseAsset": "BNB", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -73484,7 +121431,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", + "maxQty": "9222440.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -73492,7 +121439,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -73500,7 +121447,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1117.77475000", + "maxQty": "2875.69293259", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73527,24 +121474,94 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ZAR", + "quoteAsset": "EUR", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBZAR" + "symbol": "SOLEUR" }, { - "baseAsset": "USDT", + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2628019953.48", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SHIBBRL" + }, + { + "baseAsset": "AGIX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -73554,15 +121571,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -73570,7 +121587,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "30129.96826389", + "maxQty": "585408.68867268", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73597,24 +121614,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ZAR", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDTZAR" + "symbol": "AGIXBTC" }, { - "baseAsset": "BUSD", + "baseAsset": "ICP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -73624,15 +121641,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -73640,7 +121657,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25545.62555556", + "maxQty": "1264.16307157", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73667,24 +121684,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ZAR", + "quoteAsset": "EUR", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BUSDZAR" + "symbol": "ICPEUR" }, { - "baseAsset": "BTC", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "700000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -73694,15 +121711,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -73710,7 +121727,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6.47489095", + "maxQty": "49433.13669214", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73737,24 +121754,94 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MATICGBP" + }, + { + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "19083782888.66", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BTCBKRW" + "status": "TRADING", + "symbol": "SHIBTRY" }, { - "baseAsset": "ETH", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "500000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -73764,15 +121851,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "8000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "184467.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -73780,7 +121867,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "34.29793919", + "maxQty": "44270.59416261", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73807,24 +121894,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "ETHBKRW" + "quotePrecision": 2, + "status": "TRADING", + "symbol": "MATICBIDR" }, { - "baseAsset": "BNB", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "5000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -73834,15 +121921,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -73850,7 +121937,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "471.04390139", + "maxQty": "14795.25378735", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73877,24 +121964,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BNBBKRW" + "status": "TRADING", + "symbol": "MATICRUB" }, { - "baseAsset": "WTC", + "baseAsset": "NU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -73904,15 +121991,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -73920,7 +122007,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "224402.08463889", + "maxQty": "72123.91250000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -73947,24 +122034,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "WTCUSDT" + "status": "BREAK", + "symbol": "NUBTC" }, { - "baseAsset": "DATA", + "baseAsset": "NU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -73974,15 +122061,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -73990,7 +122077,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "980177.80416667", + "maxQty": "46137.09444444", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74017,24 +122104,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "DATABUSD" + "status": "BREAK", + "symbol": "NUBNB" }, { - "baseAsset": "DATA", + "baseAsset": "NU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -74044,9 +122131,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -74060,7 +122147,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "903184.94909722", + "maxQty": "73245.72777777", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74087,24 +122174,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "DATAUSDT" + "status": "BREAK", + "symbol": "NUBUSD" }, { - "baseAsset": "XZC", + "baseAsset": "NU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -74114,9 +122201,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -74130,7 +122217,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18526.19352431", + "maxQty": "223787.46944444", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74163,16 +122250,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "XZCUSDT" + "symbol": "NUUSDT" }, { - "baseAsset": "SOL", + "baseAsset": "XVG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.00001000", "tickSize": "0.00001000" }, @@ -74184,15 +122271,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74200,7 +122287,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3755.61812500", + "maxQty": "7349411.30993745", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74227,24 +122314,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SOLBNB" + "symbol": "XVGUSDT" }, { - "baseAsset": "SOL", + "baseAsset": "RLC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -74254,15 +122341,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74270,7 +122357,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48589.39444444", + "maxQty": "14027.53606671", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74297,24 +122384,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SOLBTC" + "symbol": "RLCBUSD" }, { - "baseAsset": "SOL", + "baseAsset": "CELR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -74324,9 +122411,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -74340,7 +122427,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "115016.95065278", + "maxQty": "1701908.15149409", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74367,24 +122454,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SOLUSDT" + "symbol": "CELRBUSD" }, { - "baseAsset": "SOL", + "baseAsset": "ATM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -74394,7 +122481,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -74410,7 +122497,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "30150.90100694", + "maxQty": "2151.24860319", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74443,18 +122530,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SOLBUSD" + "symbol": "ATMBUSD" }, { - "baseAsset": "BTC", + "baseAsset": "ZEN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "9000000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -74464,15 +122551,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74480,7 +122567,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1.15672238", + "maxQty": "778.06803335", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74507,24 +122594,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "IDRT", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCIDRT" + "symbol": "ZENBUSD" }, { - "baseAsset": "BNB", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "50000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -74534,15 +122621,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74550,7 +122637,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1429.61625486", + "maxQty": "352682.70674079", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74564,7 +122651,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -74575,26 +122662,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "IDRT", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBIDRT" + "symbol": "FTMBUSD" }, { - "baseAsset": "USDT", + "baseAsset": "THETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -74604,15 +122692,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74620,7 +122708,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "111262.44304861", + "maxQty": "73923.70548992", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74647,24 +122735,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "IDRT", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "USDTIDRT" + "symbol": "THETABUSD" }, { - "baseAsset": "BUSD", + "baseAsset": "WIN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "100.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -74674,15 +122762,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "913205152.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74690,7 +122778,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "57015.43843750", + "maxQty": "79285416.72897845", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74704,7 +122792,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -74715,26 +122803,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "IDRT", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "BUSDIDRT" + "symbol": "WINBUSD" }, { - "baseAsset": "CTSI", + "baseAsset": "KAVA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -74744,15 +122833,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -74760,7 +122849,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "998076.94722222", + "maxQty": "9339.38644892", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74787,24 +122876,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTSIBTC" + "symbol": "KAVABUSD" }, { - "baseAsset": "CTSI", + "baseAsset": "XEM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -74814,9 +122903,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -74830,7 +122919,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1193054.76951389", + "maxQty": "145522.78194444", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74857,24 +122946,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "CTSIUSDT" + "status": "BREAK", + "symbol": "XEMBUSD" }, { - "baseAsset": "CTSI", + "baseAsset": "ATA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -74884,7 +122973,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -74892,7 +122981,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -74900,7 +122989,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "108639.55833333", + "maxQty": "28007.98401667", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74914,7 +123003,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -74925,26 +123014,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTSIBNB" + "symbol": "ATABTC" }, { - "baseAsset": "CTSI", + "baseAsset": "ATA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -74954,15 +123044,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -74970,7 +123060,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "169359.38965278", + "maxQty": "51183.28978457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -74997,24 +123087,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTSIBUSD" + "symbol": "ATABNB" }, { - "baseAsset": "HIVE", + "baseAsset": "ATA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -75024,15 +123114,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -75040,7 +123130,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "167070.80693481", + "maxQty": "37645.62612925", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75054,7 +123144,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -75065,26 +123155,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "HIVEBNB" + "status": "TRADING", + "symbol": "ATABUSD" }, { - "baseAsset": "HIVE", + "baseAsset": "ATA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -75094,7 +123185,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9222449.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -75102,7 +123193,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -75110,7 +123201,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "194177.42291667", + "maxQty": "149266.12161223", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75124,7 +123215,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -75135,26 +123226,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HIVEBTC" + "symbol": "ATAUSDT" }, { - "baseAsset": "HIVE", + "baseAsset": "GTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -75164,15 +123256,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -75180,7 +123272,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "320645.75084722", + "maxQty": "2000.55733148", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75194,7 +123286,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -75205,26 +123297,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HIVEUSDT" + "symbol": "GTCBTC" }, { - "baseAsset": "CHR", + "baseAsset": "GTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -75234,15 +123327,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -75250,7 +123343,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "273091.17500000", + "maxQty": "1878.81038461", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75282,19 +123375,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "CHRBNB" + "status": "BREAK", + "symbol": "GTCBNB" }, { - "baseAsset": "CHR", + "baseAsset": "GTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -75304,15 +123397,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -75320,7 +123413,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1671397.45555556", + "maxQty": "3145.57063933", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75348,24 +123441,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CHRBTC" + "symbol": "GTCBUSD" }, { - "baseAsset": "CHR", + "baseAsset": "GTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -75375,7 +123468,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -75391,7 +123484,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2265090.90875000", + "maxQty": "17834.78026407", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75425,18 +123518,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CHRUSDT" + "symbol": "GTCUSDT" }, { - "baseAsset": "BTCUP", + "baseAsset": "TORN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "127.95200000", - "minPrice": "6.73500000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -75446,7 +123539,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -75454,7 +123547,77 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "591.97327310", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "TORNBTC" + }, + { + "baseAsset": "TORN", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -75462,7 +123625,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6525.94608333", + "maxQty": "515.78292682", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75477,7 +123640,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -75487,26 +123650,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "BTCUPUSDT" + "status": "BREAK", + "symbol": "TORNBNB" }, { - "baseAsset": "BTCDOWN", + "baseAsset": "TORN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.54870000", - "minPrice": "0.02890000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -75516,7 +123679,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "922327.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -75532,7 +123695,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1559388.20584028", + "maxQty": "1080.85474635", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75547,7 +123710,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -75557,26 +123720,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCDOWNUSDT" + "symbol": "TORNBUSD" }, { - "baseAsset": "GXS", + "baseAsset": "TORN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -75586,7 +123749,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "922327.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -75602,7 +123765,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "111209.26422917", + "maxQty": "4285.44568450", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75635,18 +123798,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GXSUSDT" + "symbol": "TORNUSDT" }, { - "baseAsset": "ARDR", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -75656,7 +123819,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -75672,7 +123835,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "314219.20381944", + "maxQty": "37823.07324530", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75699,24 +123862,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ARDRUSDT" + "symbol": "MATICTRY" }, { - "baseAsset": "ERD", + "baseAsset": "ETC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -75726,9 +123889,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -75742,7 +123905,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7251597.19596942", + "maxQty": "859.86816666", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75769,24 +123932,94 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "GBP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "ERDBUSD" + "symbol": "ETCGBP" }, { - "baseAsset": "LEND", + "baseAsset": "SOL", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "863.35483669", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "GBP", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SOLGBP" + }, + { + "baseAsset": "BAKE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -75796,7 +124029,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -75804,7 +124037,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -75812,7 +124045,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1798891.90756944", + "maxQty": "54967.60555941", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75840,24 +124073,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "LENDUSDT" + "status": "TRADING", + "symbol": "BAKEBTC" }, { - "baseAsset": "HBAR", + "baseAsset": "COTI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -75867,9 +124100,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -75883,7 +124116,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1222779.57638889", + "maxQty": "311152.79221681", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75897,7 +124130,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -75908,7 +124141,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -75916,18 +124150,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HBARBUSD" + "symbol": "COTIBUSD" }, { - "baseAsset": "MATIC", + "baseAsset": "KEEP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -75937,15 +124171,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -75953,7 +124187,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2657370.96930556", + "maxQty": "74670.32638888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -75980,24 +124214,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "MATICBUSD" + "status": "BREAK", + "symbol": "KEEPBTC" }, { - "baseAsset": "WRX", + "baseAsset": "KEEP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -76007,9 +124241,79 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "69703.30000000", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "BREAK", + "symbol": "KEEPBNB" + }, + { + "baseAsset": "KEEP", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -76023,7 +124327,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "344666.70833333", + "maxQty": "124899.17638888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76055,19 +124359,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "WRXBUSD" + "status": "BREAK", + "symbol": "KEEPBUSD" }, { - "baseAsset": "ZIL", + "baseAsset": "KEEP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -76077,9 +124381,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -76093,7 +124397,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2744029.70395833", + "maxQty": "585131.83750000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76120,24 +124424,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZILBUSD" + "status": "BREAK", + "symbol": "KEEPUSDT" }, { - "baseAsset": "MDT", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "30000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -76147,15 +124451,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "3074353.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -76163,7 +124467,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "336429.52026144", + "maxQty": "1381.33578179", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76190,24 +124494,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "MDTBNB" + "status": "TRADING", + "symbol": "SOLTRY" }, { - "baseAsset": "MDT", + "baseAsset": "RUNE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -76217,15 +124521,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -76233,7 +124537,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5254983.72222222", + "maxQty": "5056.94614315", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76260,24 +124564,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "GBP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MDTBTC" + "symbol": "RUNEGBP" }, { - "baseAsset": "MDT", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -76287,9 +124591,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -76303,7 +124607,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1128713.71986111", + "maxQty": "428.66533009", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76330,24 +124634,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MDTUSDT" + "symbol": "SOLBRL" }, { - "baseAsset": "STMX", + "baseAsset": "SC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -76357,7 +124661,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -76365,7 +124669,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -76373,7 +124677,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2403162.48333333", + "maxQty": "1182396.95552466", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76387,7 +124691,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -76398,26 +124702,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STMXBNB" + "symbol": "SCBUSD" }, { - "baseAsset": "STMX", + "baseAsset": "CHR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -76427,7 +124732,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "922327.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -76435,7 +124740,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -76443,7 +124748,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "260176694.04444444", + "maxQty": "120057.73314801", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76457,7 +124762,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -76468,15 +124773,16 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STMXBTC" + "symbol": "CHRBUSD" }, { "baseAsset": "STMX", @@ -76485,9 +124791,9 @@ "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -76497,7 +124803,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9222449.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -76505,7 +124811,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -76513,7 +124819,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3231254.77569444", + "maxQty": "1802117.54134815", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76540,24 +124846,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STMXETH" + "symbol": "STMXBUSD" }, { - "baseAsset": "STMX", + "baseAsset": "HNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -76567,9 +124873,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -76583,7 +124889,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9288578.43958333", + "maxQty": "5450.13403057", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76610,24 +124916,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STMXUSDT" + "symbol": "HNTBUSD" }, { - "baseAsset": "KNC", + "baseAsset": "FTT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -76637,9 +124943,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -76653,7 +124959,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "75031.24879722", + "maxQty": "7314.02507991", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76686,18 +124992,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KNCBUSD" + "symbol": "FTTBUSD" }, { - "baseAsset": "KNC", + "baseAsset": "DOCK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -76707,9 +125013,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -76723,7 +125029,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "151171.09573889", + "maxQty": "308749.38012508", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76737,7 +125043,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -76748,27 +125054,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KNCUSDT" + "symbol": "DOCKBUSD" }, { - "baseAsset": "REP", + "baseAsset": "ADA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "500000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -76778,15 +125083,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "184467.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -76794,7 +125099,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9858.55033818", + "maxQty": "49789.05934676", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76821,24 +125126,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "REPBUSD" + "quotePrecision": 2, + "status": "TRADING", + "symbol": "ADABIDR" }, { - "baseAsset": "REP", + "baseAsset": "ERN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -76848,15 +125153,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -76864,7 +125169,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3939.39011458", + "maxQty": "5903.16066712", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76878,7 +125183,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -76889,27 +125194,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "REPUSDT" + "symbol": "ERNBNB" }, { - "baseAsset": "LRC", + "baseAsset": "ERN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -76919,7 +125223,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -76935,7 +125239,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "127391.60763889", + "maxQty": "6826.23217512", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -76949,7 +125253,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -76960,7 +125264,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -76968,18 +125273,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LRCBUSD" + "symbol": "ERNBUSD" }, { - "baseAsset": "LRC", + "baseAsset": "ERN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -76989,7 +125294,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -77005,7 +125310,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "646228.99444444", + "maxQty": "19327.11876302", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77039,18 +125344,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LRCUSDT" + "symbol": "ERNUSDT" }, { - "baseAsset": "IQ", + "baseAsset": "KLAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -77060,15 +125365,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -77076,7 +125381,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2667189.17152778", + "maxQty": "31497.27943015", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77103,16 +125408,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "IQBNB" + "symbol": "KLAYBTC" }, { - "baseAsset": "IQ", + "baseAsset": "KLAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -77130,7 +125435,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -77138,7 +125443,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -77146,7 +125451,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1753130.92638889", + "maxQty": "20595.28353022", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77173,24 +125478,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "IQBUSD" + "symbol": "KLAYBNB" }, { - "baseAsset": "PNT", + "baseAsset": "KLAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -77200,15 +125505,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -77216,7 +125521,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50716.40069444", + "maxQty": "44861.47199444", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77244,24 +125549,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PNTBTC" + "symbol": "KLAYBUSD" }, { - "baseAsset": "PNT", + "baseAsset": "KLAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -77271,9 +125576,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -77287,7 +125592,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "107984.67531250", + "maxQty": "335366.63148019", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77321,18 +125626,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PNTUSDT" + "symbol": "KLAYUSDT" }, { - "baseAsset": "BTC", + "baseAsset": "RUNE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -77342,9 +125647,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -77358,7 +125663,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50.38579653", + "maxQty": "6136.09826268", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77372,7 +125677,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -77383,27 +125688,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "GBP", + "quoteAsset": "EUR", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCGBP" + "symbol": "RUNEEUR" }, { - "baseAsset": "ETH", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -77413,9 +125717,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -77429,7 +125733,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "616.56655165", + "maxQty": "50092.18714384", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77443,7 +125747,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -77454,27 +125758,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "GBP", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHGBP" + "symbol": "MATICAUD" }, { - "baseAsset": "XRP", + "baseAsset": "DOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "999996.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -77484,15 +125787,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92233.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -77500,7 +125803,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "474715.41597222", + "maxQty": "1011.23175121", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77527,24 +125830,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "GBP", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPGBP" + "symbol": "DOTRUB" }, { - "baseAsset": "BNB", + "baseAsset": "UTK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -77554,9 +125857,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -77570,7 +125873,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2990.52138889", + "maxQty": "100968.87908269", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77597,24 +125900,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "GBP", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBGBP" + "symbol": "UTKBUSD" }, { - "baseAsset": "GBP", + "baseAsset": "IOTX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -77624,9 +125927,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -77640,7 +125943,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "398134.73940972", + "maxQty": "1430443.78804725", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77674,18 +125977,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GBPBUSD" + "symbol": "IOTXBUSD" }, { - "baseAsset": "DGB", + "baseAsset": "PHA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -77695,7 +125998,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -77703,7 +126006,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -77711,7 +126014,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "639356.08611111", + "maxQty": "403108.74079221", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77738,24 +126041,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DGBBNB" + "symbol": "PHAUSDT" }, { - "baseAsset": "DGB", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "999996.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -77765,15 +126068,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92233.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -77781,7 +126084,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16454972.94166667", + "maxQty": "339.05051424", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77795,7 +126098,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -77806,27 +126109,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DGBBTC" + "symbol": "SOLRUB" }, { - "baseAsset": "DGB", + "baseAsset": "RUNE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -77836,9 +126138,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.00000100", + "stepSize": "0.00000100" }, { "applyToMarket": true, @@ -77852,7 +126154,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "465012.93465278", + "maxQty": "2762.94655564", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77879,24 +126181,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "DGBBUSD" + "status": "BREAK", + "symbol": "RUNEAUD" }, { - "baseAsset": "BTC", + "baseAsset": "BUSD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -77906,9 +126208,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -77922,7 +126224,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2.57541690", + "maxQty": "174683.93189715", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -77955,18 +126257,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCUAH" + "symbol": "BUSDUAH" }, { - "baseAsset": "USDT", + "baseAsset": "BOND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -77976,15 +126278,86 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4319.71072967", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BONDBTC" + }, + { + "baseAsset": "BOND", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -77992,7 +126365,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "127039.63895833", + "maxQty": "1677.18542307", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78019,24 +126392,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "UAH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "USDTUAH" + "status": "BREAK", + "symbol": "BONDBNB" }, { - "baseAsset": "COMP", + "baseAsset": "BOND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -78046,15 +126419,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78062,7 +126435,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "753.37325417", + "maxQty": "1172.39378735", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78090,92 +126463,22 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "COMPBTC" - }, - { - "baseAsset": "COMP", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "55.28267083", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "COMPBNB" + "symbol": "BONDBUSD" }, { - "baseAsset": "COMP", + "baseAsset": "BOND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -78187,9 +126490,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -78203,7 +126506,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "676.16349481", + "maxQty": "11381.48930507", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78217,7 +126520,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -78228,26 +126531,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "COMPBUSD" + "symbol": "BONDUSDT" }, { - "baseAsset": "COMP", + "baseAsset": "MLN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -78257,15 +126561,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -78273,7 +126577,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2094.57760771", + "maxQty": "231.42034329", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78301,24 +126605,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "COMPUSDT" + "symbol": "MLNBTC" }, { - "baseAsset": "BTC", + "baseAsset": "MLN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "9000000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -78328,15 +126632,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -78344,7 +126648,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3.93634570", + "maxQty": "404.77304933", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78371,24 +126675,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCBIDR" + "symbol": "MLNBNB" }, { - "baseAsset": "ETH", + "baseAsset": "MLN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "300000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -78398,15 +126702,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78414,7 +126718,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "112.59398119", + "maxQty": "447.44107574", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78428,7 +126732,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -78439,26 +126743,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHBIDR" + "symbol": "MLNBUSD" }, { - "baseAsset": "BNB", + "baseAsset": "MLN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "50000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -78468,7 +126773,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -78476,7 +126781,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78484,7 +126789,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1161.72814861", + "maxQty": "1273.26752744", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78498,7 +126803,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -78509,26 +126814,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBBIDR" + "symbol": "MLNUSDT" }, { - "baseAsset": "BUSD", + "baseAsset": "GRT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -78538,15 +126844,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78554,7 +126860,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "29502.13742361", + "maxQty": "43406.23558026", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78581,24 +126887,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "BUSDBIDR" + "symbol": "GRTTRY" }, { - "baseAsset": "USDT", + "baseAsset": "CAKE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -78608,7 +126914,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -78616,7 +126922,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78624,7 +126930,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "119923.23743750", + "maxQty": "1751.15850000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78651,24 +126957,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BRL", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, - "status": "TRADING", - "symbol": "USDTBIDR" + "quotePrecision": 8, + "status": "BREAK", + "symbol": "CAKEBRL" }, { - "baseAsset": "BKRW", + "baseAsset": "ICP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "999996.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -78678,15 +126984,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92233.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78694,7 +127000,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61102615.56557946", + "maxQty": "178.52233333", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78721,24 +127027,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "BREAK", - "symbol": "BKRWUSDT" + "symbol": "ICPRUB" }, { - "baseAsset": "BKRW", + "baseAsset": "DOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -78748,9 +127054,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -78764,7 +127070,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48969448.15395284", + "maxQty": "2990.08021542", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78791,24 +127097,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BKRWBUSD" + "status": "TRADING", + "symbol": "DOTAUD" }, { - "baseAsset": "SC", + "baseAsset": "AAVE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "999996.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -78818,9 +127124,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92233.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -78834,7 +127140,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20626664.13194444", + "maxQty": "16.90000694", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78848,7 +127154,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -78859,25 +127165,24 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "SCUSDT" + "status": "BREAK", + "symbol": "AAVEBRL" }, { - "baseAsset": "ZEN", + "baseAsset": "EOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -78889,9 +127194,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -78905,7 +127210,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7252.20577847", + "maxQty": "1583.62763888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78932,24 +127237,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "ZENUSDT" + "status": "BREAK", + "symbol": "EOSAUD" }, { - "baseAsset": "SXP", + "baseAsset": "DEXE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -78959,15 +127264,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -78975,7 +127280,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "281634.21111111", + "maxQty": "8264.11395413", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -78989,7 +127294,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -79000,27 +127305,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPBTC" + "symbol": "DEXEUSDT" }, { - "baseAsset": "SXP", + "baseAsset": "LTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -79030,15 +127334,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -79046,7 +127350,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23583.14659722", + "maxQty": "167997.22237665", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79073,16 +127377,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPBNB" + "symbol": "LTOBUSD" }, { - "baseAsset": "SXP", + "baseAsset": "ADX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -79100,9 +127404,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -79116,7 +127420,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "151467.80559028", + "maxQty": "36901.00486448", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79149,18 +127453,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPBUSD" + "symbol": "ADXBUSD" }, { - "baseAsset": "SNX", + "baseAsset": "QUICK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -79170,9 +127474,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -79186,7 +127490,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "37387.74621528", + "maxQty": "103.12436761", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79220,18 +127524,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SNXBTC" + "symbol": "QUICKBTC" }, { - "baseAsset": "SNX", + "baseAsset": "QUICK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -79241,15 +127545,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -79257,7 +127561,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6257.07147917", + "maxQty": "64.08490965", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79290,18 +127594,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SNXBNB" + "symbol": "QUICKBNB" }, { - "baseAsset": "SNX", + "baseAsset": "QUICK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -79311,7 +127615,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "922327.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -79327,7 +127631,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18707.56737778", + "maxQty": "190.12027866", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79341,7 +127645,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -79352,7 +127656,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -79360,16 +127665,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SNXBUSD" + "symbol": "QUICKBUSD" }, { - "baseAsset": "SNX", + "baseAsset": "C98", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -79381,9 +127686,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -79397,7 +127702,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48266.32259931", + "maxQty": "169450.87303683", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79431,17 +127736,17 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SNXUSDT" + "symbol": "C98USDT" }, { - "baseAsset": "ETHUP", + "baseAsset": "C98", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "200.95500000", - "minPrice": "10.57700000", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", "tickSize": "0.00100000" }, { @@ -79452,9 +127757,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -79468,7 +127773,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4751.31252778", + "maxQty": "37332.95441278", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79482,8 +127787,8 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -79493,26 +127798,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHUPUSDT" + "symbol": "C98BUSD" }, { - "baseAsset": "ETHDOWN", + "baseAsset": "C98", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.03322000", - "minPrice": "0.00175000", - "tickSize": "0.00001000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -79522,15 +127828,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -79538,7 +127844,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9981539.79575000", + "maxQty": "11833.21264767", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79553,7 +127859,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -79563,26 +127869,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHDOWNUSDT" + "symbol": "C98BNB" }, { - "baseAsset": "ADAUP", + "baseAsset": "C98", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "22.32800000", - "minPrice": "1.17600000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -79592,15 +127898,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -79608,7 +127914,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5172.50965972", + "maxQty": "22078.08964558", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79622,8 +127928,8 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -79633,26 +127939,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ADAUPUSDT" + "symbol": "C98BTC" }, { - "baseAsset": "ADADOWN", + "baseAsset": "CLV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.08864000", - "minPrice": "0.00467000", - "tickSize": "0.00001000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -79662,15 +127969,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -79678,7 +127985,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "933553.78154167", + "maxQty": "100133.44301598", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79692,8 +127999,8 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -79703,26 +128010,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ADADOWNUSDT" + "symbol": "CLVBTC" }, { - "baseAsset": "LINKUP", + "baseAsset": "CLV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "29.69800000", - "minPrice": "1.56400000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -79732,15 +128040,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -79748,7 +128056,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8785.40199306", + "maxQty": "84171.94308547", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79763,7 +128071,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -79773,26 +128081,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKUPUSDT" + "symbol": "CLVBNB" }, { - "baseAsset": "LINKDOWN", + "baseAsset": "CLV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.00689500", - "minPrice": "0.00036300", - "tickSize": "0.00000100" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -79802,9 +128110,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -79818,7 +128126,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15223467.16421528", + "maxQty": "94351.84433634", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79832,8 +128140,8 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -79843,26 +128151,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKDOWNUSDT" + "symbol": "CLVBUSD" }, { - "baseAsset": "VTHO", + "baseAsset": "CLV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -79872,15 +128181,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -79888,7 +128197,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5664610.74513889", + "maxQty": "345778.03356497", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79902,7 +128211,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -79913,18 +128222,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "VTHOBNB" + "symbol": "CLVUSDT" }, { - "baseAsset": "VTHO", + "baseAsset": "QNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -79942,15 +128252,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -79958,7 +128268,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "37909546.95988935", + "maxQty": "1154.87536344", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -79972,7 +128282,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -79983,26 +128293,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "VTHOBUSD" + "status": "TRADING", + "symbol": "QNTBTC" }, { - "baseAsset": "VTHO", + "baseAsset": "QNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -80012,15 +128323,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -80028,7 +128339,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "36925951.74375000", + "maxQty": "350.74807574", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80055,24 +128366,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "VTHOUSDT" + "symbol": "QNTBNB" }, { - "baseAsset": "DCR", + "baseAsset": "QNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -80082,7 +128393,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "922327.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -80098,7 +128409,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "967.25717012", + "maxQty": "855.05582835", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80112,7 +128423,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -80123,26 +128434,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DCRBUSD" + "status": "TRADING", + "symbol": "QNTBUSD" }, { - "baseAsset": "DGB", + "baseAsset": "QNT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -80152,9 +128464,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -80168,7 +128480,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3967844.52166667", + "maxQty": "3340.51812300", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80202,18 +128514,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DGBUSDT" + "symbol": "QNTUSDT" }, { - "baseAsset": "GBP", + "baseAsset": "FLOW", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -80223,7 +128535,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -80231,7 +128543,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -80239,7 +128551,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "524712.61595139", + "maxQty": "15806.51114662", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80267,24 +128579,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GBPUSDT" + "symbol": "FLOWBTC" }, { - "baseAsset": "STORJ", + "baseAsset": "FLOW", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -80294,7 +128606,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -80302,7 +128614,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -80310,7 +128622,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20327.64335948", + "maxQty": "6712.08624739", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80337,24 +128649,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "STORJBUSD" + "status": "TRADING", + "symbol": "FLOWBNB" }, { - "baseAsset": "SXP", + "baseAsset": "FLOW", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -80364,9 +128676,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -80380,7 +128692,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "803307.64525625", + "maxQty": "15587.51567060", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80408,24 +128720,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPUSDT" + "symbol": "FLOWBUSD" }, { - "baseAsset": "IRIS", + "baseAsset": "FLOW", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -80435,15 +128747,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -80451,7 +128763,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "85086.21045752", + "maxQty": "52900.63663655", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80465,7 +128777,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -80476,24 +128788,25 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "IRISBNB" + "status": "TRADING", + "symbol": "FLOWUSDT" }, { - "baseAsset": "IRIS", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, + "baseAsset": "XEC", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "1.00000000", "minPrice": "0.00000001", "tickSize": "0.00000001" }, @@ -80505,79 +128818,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "986012.35625000", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "IRISBTC" - }, - { - "baseAsset": "IRIS", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" }, { "applyToMarket": true, @@ -80591,9 +128834,9 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "78319.48723994", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "maxQty": "779218691.10", + "minQty": "0.00", + "stepSize": "0.00" }, { "filterType": "MAX_NUM_ORDERS", @@ -80605,7 +128848,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -80616,26 +128859,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "IRISBUSD" + "status": "TRADING", + "symbol": "XECBUSD" }, { - "baseAsset": "MKR", + "baseAsset": "AXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -80645,15 +128889,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -80661,7 +128905,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "14.04915000", + "maxQty": "335.45376650", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80688,24 +128932,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MKRBNB" + "symbol": "AXSBRL" }, { - "baseAsset": "MKR", + "baseAsset": "AXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -80715,15 +128959,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -80731,7 +128975,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "83.31616250", + "maxQty": "255.27082001", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80745,7 +128989,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -80756,27 +129000,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MKRBTC" + "symbol": "AXSAUD" }, { - "baseAsset": "MKR", + "baseAsset": "TVK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -80786,9 +129029,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -80802,7 +129045,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "132.91418762", + "maxQty": "582577.58304378", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80836,18 +129079,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MKRUSDT" + "symbol": "TVKUSDT" }, { - "baseAsset": "MKR", + "baseAsset": "MINA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -80857,15 +129100,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -80873,7 +129116,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "87.74438195", + "maxQty": "48140.04725503", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80887,7 +129130,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -80898,26 +129141,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MKRBUSD" + "symbol": "MINABTC" }, { - "baseAsset": "DAI", + "baseAsset": "MINA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -80927,7 +129171,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -80935,7 +129179,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -80943,7 +129187,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13225.87375000", + "maxQty": "20465.71716469", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -80975,19 +129219,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DAIBNB" + "status": "TRADING", + "symbol": "MINABNB" }, { - "baseAsset": "DAI", + "baseAsset": "MINA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -80997,15 +129241,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -81013,7 +129257,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "64544.98819444", + "maxQty": "49603.68860319", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81027,7 +129271,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -81038,26 +129282,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DAIBTC" + "status": "TRADING", + "symbol": "MINABUSD" }, { - "baseAsset": "DAI", + "baseAsset": "MINA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -81067,9 +129312,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -81083,7 +129328,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "452151.67404167", + "maxQty": "157347.12967338", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81097,7 +129342,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -81108,26 +129353,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DAIUSDT" + "status": "TRADING", + "symbol": "MINAUSDT" }, { - "baseAsset": "DAI", + "baseAsset": "RAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -81137,15 +129383,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -81153,7 +129399,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "119396.14040278", + "maxQty": "19749.65239749", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81180,24 +129426,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DAIBUSD" + "status": "TRADING", + "symbol": "RAYBNB" }, { - "baseAsset": "RUNE", + "baseAsset": "RAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -81207,7 +129453,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -81215,7 +129461,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -81223,7 +129469,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8208.14236111", + "maxQty": "13538.79041000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81237,7 +129483,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -81248,26 +129494,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RUNEBNB" + "symbol": "RAYBUSD" }, { - "baseAsset": "RUNE", + "baseAsset": "RAY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -81277,15 +129524,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -81293,7 +129540,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "58967.57222222", + "maxQty": "30438.71355107", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81307,7 +129554,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -81318,26 +129565,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RUNEBTC" + "symbol": "RAYUSDT" }, { - "baseAsset": "RUNE", + "baseAsset": "FARM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -81347,15 +129595,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -81363,7 +129611,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48841.59040278", + "maxQty": "393.46146282", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81390,24 +129638,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RUNEBUSD" + "symbol": "FARMBTC" }, { - "baseAsset": "MANA", + "baseAsset": "FARM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -81417,15 +129665,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -81433,7 +129681,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "523840.45777778", + "maxQty": "189.33194996", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81460,24 +129708,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MANABUSD" + "symbol": "FARMBNB" }, { - "baseAsset": "DOGE", + "baseAsset": "FARM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -81487,9 +129735,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -81503,7 +129751,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9802431.65486111", + "maxQty": "298.15815983", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81536,18 +129784,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOGEBUSD" + "symbol": "FARMBUSD" }, { - "baseAsset": "LEND", + "baseAsset": "FARM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -81557,9 +129805,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -81573,7 +129821,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "270430.73030556", + "maxQty": "1136.01079082", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81600,24 +129848,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "LENDBUSD" + "status": "TRADING", + "symbol": "FARMUSDT" }, { - "baseAsset": "ZRX", + "baseAsset": "ALPACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -81627,15 +129875,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -81643,7 +129891,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "179329.04180556", + "maxQty": "36300.53064628", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81670,24 +129918,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ZRXBUSD" + "symbol": "ALPACABTC" }, { - "baseAsset": "DCR", + "baseAsset": "ALPACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -81697,15 +129945,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -81713,7 +129961,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1048.53362222", + "maxQty": "39907.28728283", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81740,16 +129988,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DCRUSDT" + "symbol": "ALPACABNB" }, { - "baseAsset": "STORJ", + "baseAsset": "ALPACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -81767,9 +130015,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -81783,7 +130031,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "280407.66855556", + "maxQty": "62872.95211952", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81797,7 +130045,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -81808,27 +130056,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STORJUSDT" + "symbol": "ALPACABUSD" }, { - "baseAsset": "XRP", + "baseAsset": "ALPACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -81838,7 +130085,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -81846,7 +130093,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -81854,7 +130101,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "29020.38854167", + "maxQty": "150903.95767894", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81881,24 +130128,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "XRPBKRW" + "status": "TRADING", + "symbol": "ALPACAUSDT" }, { - "baseAsset": "ADA", + "baseAsset": "TLM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -81908,15 +130155,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -81924,7 +130171,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "101738.55506944", + "maxQty": "656748.26268241", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -81951,24 +130198,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "ADABKRW" + "status": "TRADING", + "symbol": "TLMTRY" }, { - "baseAsset": "BTC", + "baseAsset": "QUICK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -81978,9 +130225,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -81994,7 +130241,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "34.54352545", + "maxQty": "321.66767477", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82008,7 +130255,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -82019,26 +130266,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "AUD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCAUD" + "symbol": "QUICKUSDT" }, { - "baseAsset": "ETH", + "baseAsset": "ORN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -82048,9 +130296,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -82064,7 +130312,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "275.40352879", + "maxQty": "3356.33092425", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82091,24 +130339,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "AUD", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHAUD" + "symbol": "ORNBUSD" }, { - "baseAsset": "AUD", + "baseAsset": "MBOX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -82118,7 +130366,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -82126,7 +130374,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -82134,7 +130382,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "302462.05090278", + "maxQty": "11651.13168867", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82148,7 +130396,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -82159,26 +130407,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AUDBUSD" + "symbol": "MBOXBTC" }, { - "baseAsset": "FIO", + "baseAsset": "MBOX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -82188,7 +130437,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -82196,7 +130445,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -82204,7 +130453,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "206662.80229167", + "maxQty": "15883.58589298", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82237,18 +130486,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FIOBNB" + "symbol": "MBOXBNB" }, { - "baseAsset": "FIO", + "baseAsset": "MBOX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -82258,15 +130507,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -82274,7 +130523,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "550937.38958333", + "maxQty": "24690.16282140", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82288,7 +130537,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -82299,26 +130548,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FIOBTC" + "symbol": "MBOXBUSD" }, { - "baseAsset": "FIO", + "baseAsset": "MBOX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -82328,9 +130578,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -82344,7 +130594,78 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "119870.29665972", + "maxQty": "74237.61591382", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "MBOXUSDT" + }, + { + "baseAsset": "VGX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "45706.97289784", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82371,24 +130692,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FIOBUSD" + "symbol": "VGXBTC" }, { - "baseAsset": "BNBUP", + "baseAsset": "VGX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "59.54000000", - "minPrice": "3.13400000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -82398,15 +130719,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -82414,7 +130735,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7368.95465278", + "maxQty": "70350.83592772", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82429,7 +130750,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -82439,26 +130760,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBUPUSDT" + "symbol": "VGXETH" }, { - "baseAsset": "BNBDOWN", + "baseAsset": "FOR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.82720000", - "minPrice": "0.04360000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -82468,9 +130789,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -82484,7 +130805,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "447897.85285417", + "maxQty": "1709240.76233495", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82499,7 +130820,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -82509,7 +130830,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -82517,18 +130838,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBDOWNUSDT" + "symbol": "FORUSDT" }, { - "baseAsset": "XTZUP", + "baseAsset": "REQ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1.92500000", - "minPrice": "0.10200000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -82538,9 +130859,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -82554,7 +130875,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "200031.45994444", + "maxQty": "435884.43432939", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82569,7 +130890,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -82579,7 +130900,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -82587,17 +130908,17 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XTZUPUSDT" + "symbol": "REQUSDT" }, { - "baseAsset": "XTZDOWN", + "baseAsset": "GHST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1.72600000", - "minPrice": "0.09100000", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", "tickSize": "0.00100000" }, { @@ -82608,9 +130929,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -82624,7 +130945,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "225920.18229167", + "maxQty": "57453.81146629", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82639,7 +130960,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -82649,7 +130970,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -82657,18 +130978,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XTZDOWNUSDT" + "symbol": "GHSTUSDT" }, { - "baseAsset": "AVA", + "baseAsset": "TRU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -82678,15 +130999,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -82694,7 +131015,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6407.60659722", + "maxQty": "44331.29951355", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82721,24 +131042,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVABNB" + "symbol": "TRURUB" }, { - "baseAsset": "AVA", + "baseAsset": "FIS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -82748,15 +131069,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -82764,7 +131085,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26881.60470139", + "maxQty": "12357.76157053", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82791,16 +131112,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVABTC" + "symbol": "FISBRL" }, { - "baseAsset": "AVA", + "baseAsset": "WAXP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -82818,9 +131139,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -82834,7 +131155,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7360.67190972", + "maxQty": "446480.31549687", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82848,7 +131169,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -82859,26 +131180,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVABUSD" + "symbol": "WAXPUSDT" }, { - "baseAsset": "USDT", + "baseAsset": "WAXP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -82888,15 +131210,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -82904,7 +131226,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "33288.92055556", + "maxQty": "139668.87560806", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -82918,7 +131240,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -82929,26 +131251,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BKRW", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDTBKRW" + "symbol": "WAXPBUSD" }, { - "baseAsset": "BUSD", + "baseAsset": "WAXP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -82958,15 +131281,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -82974,7 +131297,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "112393.11979167", + "maxQty": "95629.61431549", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83001,24 +131324,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BUSDBKRW" + "status": "TRADING", + "symbol": "WAXPBNB" }, { - "baseAsset": "IOTA", + "baseAsset": "WAXP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -83028,15 +131351,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -83044,7 +131367,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "235557.04561111", + "maxQty": "94849.15774843", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83058,7 +131381,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -83069,26 +131392,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "IOTABUSD" + "symbol": "WAXPBTC" }, { - "baseAsset": "MANA", + "baseAsset": "TRIBE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -83098,15 +131422,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -83114,7 +131438,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "766236.39548611", + "maxQty": "27801.15566365", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83142,24 +131466,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "MANAUSDT" + "symbol": "TRIBEBTC" }, { - "baseAsset": "XRP", + "baseAsset": "TRIBE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -83169,15 +131493,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -83185,7 +131509,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "144009.63111111", + "maxQty": "28563.41000694", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83212,24 +131536,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "AUD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPAUD" + "symbol": "TRIBEBNB" }, { - "baseAsset": "BNB", + "baseAsset": "TRIBE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -83239,9 +131563,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -83255,7 +131579,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2359.56663264", + "maxQty": "30976.49687282", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83269,7 +131593,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -83280,26 +131604,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "AUD", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBAUD" + "symbol": "TRIBEBUSD" }, { - "baseAsset": "AUD", + "baseAsset": "TRIBE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -83309,9 +131634,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -83325,7 +131650,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "731352.49958333", + "maxQty": "129675.52883947", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83339,7 +131664,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -83350,7 +131675,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -83358,18 +131684,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AUDUSDT" + "symbol": "TRIBEUSDT" }, { - "baseAsset": "BAL", + "baseAsset": "GNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -83379,15 +131705,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -83395,7 +131721,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "174.62016644", + "maxQty": "160.37561084", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83422,24 +131748,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BALBNB" + "status": "TRADING", + "symbol": "GNOUSDT" }, { - "baseAsset": "BAL", + "baseAsset": "GNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -83449,15 +131775,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -83465,7 +131791,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2347.71779861", + "maxQty": "46.41139819", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83479,7 +131805,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -83490,27 +131816,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BALBTC" + "symbol": "GNOBUSD" }, { - "baseAsset": "BAL", + "baseAsset": "GNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -83520,7 +131845,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -83528,7 +131853,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -83536,7 +131861,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2326.07968750", + "maxQty": "45.53348366", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83563,24 +131888,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BALBUSD" + "symbol": "GNOBNB" }, { - "baseAsset": "YFI", + "baseAsset": "GNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -83590,15 +131915,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -83606,7 +131931,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "0.80878347", + "maxQty": "40.91267477", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83633,24 +131958,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIBNB" + "symbol": "GNOBTC" }, { - "baseAsset": "YFI", + "baseAsset": "ARPA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -83660,15 +131985,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -83676,7 +132001,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "32.44831931", + "maxQty": "1101503.49061848", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83690,7 +132015,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -83701,27 +132026,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIBTC" + "symbol": "ARPATRY" }, { - "baseAsset": "YFI", + "baseAsset": "PROM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -83731,15 +132055,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -83747,7 +132071,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9.61804812", + "maxQty": "2660.90984016", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83761,7 +132085,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -83772,27 +132096,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIBUSD" + "symbol": "PROMBTC" }, { - "baseAsset": "YFI", + "baseAsset": "MTL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -83802,9 +132125,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -83818,7 +132141,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63.58238370", + "maxQty": "12213.11473245", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83846,24 +132169,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIUSDT" + "symbol": "MTLBUSD" }, { - "baseAsset": "BLZ", + "baseAsset": "OGN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -83873,7 +132196,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -83889,7 +132212,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "346951.95656706", + "maxQty": "86243.33842946", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83921,19 +132244,90 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BLZBUSD" + "status": "TRADING", + "symbol": "OGNBUSD" }, { - "baseAsset": "KMD", + "baseAsset": "XEC", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "2371771691.85", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "XECUSDT" + }, + { + "baseAsset": "C98", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -83943,9 +132337,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -83959,7 +132353,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "55086.92722724", + "maxQty": "9188.59068797", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -83986,24 +132380,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "KMDBUSD" + "status": "TRADING", + "symbol": "C98BRL" }, { - "baseAsset": "BAL", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -84013,7 +132407,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "922327.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -84029,7 +132423,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4318.39924306", + "maxQty": "988.43045587", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84043,7 +132437,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -84054,27 +132448,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BALUSDT" + "symbol": "SOLAUD" }, { - "baseAsset": "BLZ", + "baseAsset": "XRP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "1000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -84084,7 +132477,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92233.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -84092,7 +132485,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -84100,7 +132493,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "689951.61118056", + "maxQty": "62211.13502432", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84127,24 +132520,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "BLZUSDT" + "symbol": "XRPBIDR" }, { - "baseAsset": "IRIS", + "baseAsset": "POLY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -84154,7 +132547,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -84170,7 +132563,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1408380.18715278", + "maxQty": "28728.34489228", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84197,16 +132590,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "IRISUSDT" + "symbol": "POLYBUSD" }, { - "baseAsset": "KMD", + "baseAsset": "ELF", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -84224,9 +132617,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -84240,7 +132633,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "99143.64038889", + "maxQty": "190099.17915218", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84273,18 +132666,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KMDUSDT" + "symbol": "ELFUSDT" }, { - "baseAsset": "BTC", + "baseAsset": "DYDX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -84294,9 +132687,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -84310,7 +132703,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12.54143391", + "maxQty": "99417.03457956", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84324,7 +132717,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -84335,26 +132728,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "DAI", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCDAI" + "symbol": "DYDXUSDT" }, { - "baseAsset": "ETH", + "baseAsset": "DYDX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "10000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -84364,9 +132758,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -84380,7 +132774,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "290.01747754", + "maxQty": "26024.91463516", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84407,24 +132801,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "DAI", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHDAI" + "symbol": "DYDXBUSD" }, { - "baseAsset": "BNB", + "baseAsset": "DYDX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "10.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -84434,15 +132828,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "8384883677.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -84450,7 +132844,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2371.06585556", + "maxQty": "10068.73683113", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84477,24 +132871,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "DAI", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBDAI" + "symbol": "DYDXBNB" }, { - "baseAsset": "USDT", + "baseAsset": "DYDX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -84504,7 +132898,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "46116860414.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -84512,7 +132906,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -84520,7 +132914,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "842400.06838194", + "maxQty": "22837.39346073", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84547,22 +132941,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "DAI", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDTDAI" + "symbol": "DYDXBTC" }, { - "baseAsset": "BUSD", + "baseAsset": "ELF", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "100.00000000", "minPrice": "0.00010000", "tickSize": "0.00010000" }, @@ -84574,9 +132968,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "913205152.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -84590,7 +132984,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "416573.15506944", + "maxQty": "38367.51813759", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84617,24 +133011,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "DAI", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BUSDDAI" + "symbol": "ELFBUSD" }, { - "baseAsset": "JST", + "baseAsset": "POLY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -84644,15 +133038,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "913205152.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -84660,7 +133054,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "217350.80486111", + "maxQty": "177142.96664350", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84687,93 +133081,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "JSTBNB" - }, - { - "baseAsset": "JST", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1922332.29097222", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "JSTBTC" + "symbol": "POLYUSDT" }, { - "baseAsset": "JST", + "baseAsset": "IDEX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "100.00000000", "minPrice": "0.00001000", "tickSize": "0.00001000" }, @@ -84785,7 +133108,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "913205152.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -84801,7 +133124,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "913224.40645833", + "maxQty": "996647.88742182", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84815,7 +133138,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -84826,26 +133149,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "JSTBUSD" + "symbol": "IDEXUSDT" }, { - "baseAsset": "JST", + "baseAsset": "VIDT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -84855,7 +133179,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -84871,7 +133195,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5574720.71020833", + "maxQty": "109867.56400277", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84885,7 +133209,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -84896,8 +133220,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -84905,18 +133228,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "JSTUSDT" + "symbol": "VIDTUSDT" }, { - "baseAsset": "SRM", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "9999998955.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -84926,15 +133249,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9223372.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -84942,7 +133265,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9204.57083333", + "maxQty": "285.06241674", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -84969,24 +133292,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "SRMBNB" + "symbol": "SOLBIDR" }, { - "baseAsset": "SRM", + "baseAsset": "AXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "9999998955.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -84996,15 +133319,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9223372.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -85012,7 +133335,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "87705.41736111", + "maxQty": "142.23145378", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85026,8 +133349,8 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": false, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -85037,27 +133360,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "TRD_GRP_003" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "SRMBTC" + "symbol": "AXSBIDR" }, { - "baseAsset": "SRM", + "baseAsset": "BTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "9999319.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -85067,9 +133389,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9223.00000000", + "minQty": "0.00001000", + "stepSize": "0.00001000" }, { "applyToMarket": true, @@ -85083,7 +133405,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "40185.53210417", + "maxQty": "6.86237712", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85110,24 +133432,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SRMBUSD" + "symbol": "BTCUSDP" }, { - "baseAsset": "SRM", + "baseAsset": "ETH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "999996.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -85137,9 +133459,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92233.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -85153,7 +133475,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "120291.17595139", + "maxQty": "55.75840257", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85167,7 +133489,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -85178,27 +133500,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "USDP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SRMUSDT" + "symbol": "ETHUSDP" }, { - "baseAsset": "ANT", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -85208,15 +133529,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -85224,7 +133545,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3686.97523611", + "maxQty": "362.53472897", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85251,34 +133572,34 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDP", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ANTBNB" + "symbol": "BNBUSDP" }, { - "baseAsset": "ANT", + "baseAsset": "USDP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1.30000000", + "minPrice": "0.70000000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "913205152.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -85286,7 +133607,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -85294,7 +133615,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13873.35316667", + "maxQty": "1942321.52954829", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85321,34 +133642,34 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ANTBTC" + "symbol": "USDPBUSD" }, { - "baseAsset": "ANT", + "baseAsset": "USDP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", + "maxPrice": "1.30000000", + "minPrice": "0.70000000", "tickSize": "0.00010000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "913205152.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -85364,7 +133685,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "11446.32819444", + "maxQty": "1930671.50072272", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85391,24 +133712,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ANTBUSD" + "symbol": "USDPUSDT" }, { - "baseAsset": "ANT", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -85418,9 +133739,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -85434,7 +133755,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18828.75708333", + "maxQty": "8209553.83113273", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85448,7 +133769,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -85459,7 +133780,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -85467,88 +133789,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ANTUSDT" + "symbol": "GALAUSDT" }, { - "baseAsset": "CRV", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "8919.78487500", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "CRVBNB" - }, - { - "baseAsset": "CRV", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -85558,15 +133810,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -85574,7 +133826,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "212741.14588889", + "maxQty": "2867914.28978457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85602,24 +133854,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CRVBTC" + "symbol": "GALABUSD" }, { - "baseAsset": "CRV", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -85629,15 +133881,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "913205152.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -85645,7 +133897,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "45795.40112292", + "maxQty": "443183.86726893", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85659,7 +133911,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -85670,27 +133922,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CRVBUSD" + "symbol": "GALABNB" }, { - "baseAsset": "CRV", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -85700,15 +133951,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "913205152.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -85716,7 +133967,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "457030.23252778", + "maxQty": "1306319.20361362", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85744,24 +133995,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CRVUSDT" + "symbol": "GALABTC" }, { - "baseAsset": "SAND", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -85771,15 +134022,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9223371114.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -85787,7 +134038,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "188150.00694444", + "maxQty": "12760.69798471", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85814,24 +134065,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "SANDBNB" + "symbol": "FTMBIDR" }, { - "baseAsset": "SAND", + "baseAsset": "ALGO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -85841,15 +134092,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9223371114.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -85857,7 +134108,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "716445.77569444", + "maxQty": "11961.87897435", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85871,7 +134122,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -85882,27 +134133,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "SANDBTC" + "quotePrecision": 2, + "status": "BREAK", + "symbol": "ALGOBIDR" }, { - "baseAsset": "SAND", + "baseAsset": "CAKE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -85912,9 +134162,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -85928,7 +134178,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "795852.92986111", + "maxQty": "1792.59415277", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -85942,7 +134192,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -85953,27 +134203,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "SANDUSDT" + "status": "BREAK", + "symbol": "CAKEAUD" }, { - "baseAsset": "SAND", + "baseAsset": "KSM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -85983,9 +134232,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -85999,7 +134248,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "486647.24097222", + "maxQty": "62.27782222", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86026,24 +134275,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "SANDBUSD" + "status": "BREAK", + "symbol": "KSMAUD" }, { - "baseAsset": "OCEAN", + "baseAsset": "WAVES", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "999996.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -86053,15 +134302,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92233.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -86069,7 +134318,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "153825.30375000", + "maxQty": "571.81791666", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86096,24 +134345,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "OCEANBNB" + "status": "BREAK", + "symbol": "WAVESRUB" }, { - "baseAsset": "OCEAN", + "baseAsset": "SUN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -86123,7 +134372,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -86131,7 +134380,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -86139,7 +134388,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "321507.49513889", + "maxQty": "1751763.76302988", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86167,24 +134416,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OCEANBTC" + "symbol": "SUNBUSD" }, { - "baseAsset": "OCEAN", + "baseAsset": "ILV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -86194,9 +134443,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -86210,7 +134459,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "142514.06955556", + "maxQty": "163.53926476", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86237,24 +134486,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OCEANBUSD" + "symbol": "ILVUSDT" }, { - "baseAsset": "OCEAN", + "baseAsset": "ILV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -86264,9 +134513,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -86280,7 +134529,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "369717.38977083", + "maxQty": "40.60160667", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86294,7 +134543,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -86305,27 +134554,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OCEANUSDT" + "symbol": "ILVBUSD" }, { - "baseAsset": "NMR", + "baseAsset": "ILV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -86335,15 +134583,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -86351,7 +134599,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "215.09227083", + "maxQty": "28.28230507", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86384,18 +134632,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NMRBNB" + "symbol": "ILVBNB" }, { - "baseAsset": "NMR", + "baseAsset": "ILV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -86405,7 +134653,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -86421,7 +134669,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1321.34752778", + "maxQty": "87.28717373", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86435,7 +134683,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -86446,8 +134694,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -86455,18 +134702,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NMRBTC" + "symbol": "ILVBTC" }, { - "baseAsset": "NMR", + "baseAsset": "REN", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -86476,9 +134723,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -86492,7 +134739,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1004.52044514", + "maxQty": "127049.69353717", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86525,16 +134772,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NMRBUSD" + "symbol": "RENBUSD" }, { - "baseAsset": "NMR", + "baseAsset": "YGG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -86546,9 +134793,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -86562,7 +134809,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1479.09867500", + "maxQty": "47121.09819318", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86596,10 +134843,81 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NMRUSDT" + "symbol": "YGGUSDT" }, { - "baseAsset": "DOT", + "baseAsset": "YGG", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "14477.38422515", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "BUSD", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "YGGBUSD" + }, + { + "baseAsset": "YGG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -86617,7 +134935,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -86625,7 +134943,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -86633,7 +134951,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4108.92979167", + "maxQty": "15371.58915913", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86666,18 +134984,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTBNB" + "symbol": "YGGBNB" }, { - "baseAsset": "DOT", + "baseAsset": "YGG", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -86687,7 +135005,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -86703,7 +135021,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "72958.96430556", + "maxQty": "15234.75892981", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86717,7 +135035,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -86728,8 +135046,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -86737,18 +135054,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTBTC" + "symbol": "YGGBTC" }, { - "baseAsset": "DOT", + "baseAsset": "STX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -86758,9 +135075,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -86774,7 +135091,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "30307.55372917", + "maxQty": "35369.53196664", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86807,10 +135124,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTBUSD" + "symbol": "STXBUSD" }, { - "baseAsset": "DOT", + "baseAsset": "SYS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -86828,9 +135145,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -86844,7 +135161,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "79100.59077083", + "maxQty": "398866.53787352", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86858,7 +135175,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -86869,8 +135186,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -86878,18 +135194,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTUSDT" + "symbol": "SYSUSDT" }, { - "baseAsset": "LUNA", + "baseAsset": "DF", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -86899,15 +135215,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -86915,7 +135231,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25501.98458333", + "maxQty": "444768.97776233", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -86942,24 +135258,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LUNABNB" + "symbol": "DFUSDT" }, { - "baseAsset": "LUNA", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -86969,15 +135285,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -86985,7 +135301,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "108953.76458333", + "maxQty": "2849.46077831", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87012,24 +135328,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LUNABTC" + "symbol": "SOLUSDC" }, { - "baseAsset": "LUNA", + "baseAsset": "ARPA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -87039,15 +135355,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -87055,7 +135371,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "30187.78902778", + "maxQty": "154974.31410701", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87082,24 +135398,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LUNABUSD" + "symbol": "ARPARUB" }, { - "baseAsset": "LUNA", + "baseAsset": "LTC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "999996.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -87109,15 +135425,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92233.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -87125,7 +135441,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "199639.67000694", + "maxQty": "211.80928492", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87152,24 +135468,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "UAH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LUNAUSDT" + "symbol": "LTCUAH" }, { - "baseAsset": "IDEX", + "baseAsset": "FET", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -87179,7 +135495,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -87187,7 +135503,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -87195,7 +135511,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "986763.76180556", + "maxQty": "279490.08408617", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87209,7 +135525,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -87220,18 +135536,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "IDEXBTC" + "symbol": "FETBUSD" }, { - "baseAsset": "IDEX", + "baseAsset": "ARPA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -87249,7 +135566,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -87265,7 +135582,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "264856.98381944", + "maxQty": "543193.07776233", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87298,18 +135615,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "IDEXBUSD" + "symbol": "ARPABUSD" }, { - "baseAsset": "RSR", + "baseAsset": "LSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -87319,15 +135636,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -87335,7 +135652,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "489634.88958333", + "maxQty": "6368.49430159", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87362,24 +135679,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RSRBNB" + "symbol": "LSKBUSD" }, { - "baseAsset": "RSR", + "baseAsset": "AVAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -87389,15 +135706,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922337194.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -87405,7 +135722,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "14806692.28541667", + "maxQty": "185.80651146", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87419,7 +135736,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -87430,27 +135747,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "RSRBTC" + "symbol": "AVAXBIDR" }, { - "baseAsset": "RSR", + "baseAsset": "ALICE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -87460,15 +135776,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922337194.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -87476,7 +135792,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2009970.73187500", + "maxQty": "1799.97430854", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87503,24 +135819,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "RSRBUSD" + "symbol": "ALICEBIDR" }, { - "baseAsset": "RSR", + "baseAsset": "FIDA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -87530,7 +135846,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -87546,7 +135862,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8442741.30131944", + "maxQty": "44881.62981236", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87560,7 +135876,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -87571,8 +135887,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -87580,10 +135895,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RSRUSDT" + "symbol": "FIDAUSDT" }, { - "baseAsset": "PAXG", + "baseAsset": "FIDA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -87601,15 +135916,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -87617,7 +135932,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "34.16649653", + "maxQty": "23106.43773453", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87644,22 +135959,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PAXGBNB" + "symbol": "FIDABUSD" }, { - "baseAsset": "PAXG", + "baseAsset": "FIDA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00001000", "tickSize": "0.00001000" }, @@ -87671,155 +135986,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "75.18351208", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "PAXGBTC" - }, - { - "baseAsset": "PAXG", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "49.69813487", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BUSD", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "BREAK", - "symbol": "PAXGBUSD" - }, - { - "baseAsset": "PAXG", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -87827,7 +136002,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "369.83891848", + "maxQty": "19527.51360718", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87854,24 +136029,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "PAXGUSDT" + "status": "BREAK", + "symbol": "FIDABNB" }, { - "baseAsset": "WNXM", + "baseAsset": "FIDA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -87881,15 +136056,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -87897,7 +136072,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "278.12292847", + "maxQty": "19578.66316886", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87924,16 +136099,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WNXMBNB" + "symbol": "FIDABTC" }, { - "baseAsset": "WNXM", + "baseAsset": "DENT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -87951,15 +136126,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -87967,7 +136142,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "959.92464722", + "maxQty": "7152806.27519110", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -87981,7 +136156,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -87992,27 +136167,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WNXMBTC" + "symbol": "DENTBUSD" }, { - "baseAsset": "WNXM", + "baseAsset": "FRONT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -88022,9 +136196,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -88038,7 +136212,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "271.96067313", + "maxQty": "129865.86031966", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88065,22 +136239,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "WNXMBUSD" + "status": "TRADING", + "symbol": "FRONTUSDT" }, { - "baseAsset": "WNXM", + "baseAsset": "CVP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -88092,9 +136266,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -88108,7 +136282,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1692.58885625", + "maxQty": "48102.02112578", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88122,7 +136296,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -88133,8 +136307,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -88142,18 +136315,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WNXMUSDT" + "symbol": "CVPUSDT" }, { - "baseAsset": "TRB", + "baseAsset": "AGLD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -88163,15 +136336,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -88179,7 +136352,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "248.28392917", + "maxQty": "21877.55170257", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88193,7 +136366,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -88204,18 +136377,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "TRBBNB" + "status": "TRADING", + "symbol": "AGLDBTC" }, { - "baseAsset": "TRB", + "baseAsset": "AGLD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -88233,15 +136407,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -88249,7 +136423,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2325.38018542", + "maxQty": "15866.47275886", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88263,7 +136437,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -88274,25 +136448,24 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRBBTC" + "symbol": "AGLDBNB" }, { - "baseAsset": "TRB", + "baseAsset": "AGLD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -88304,9 +136477,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -88320,7 +136493,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "485.25382361", + "maxQty": "33469.22633773", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88334,7 +136507,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -88345,7 +136518,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -88353,16 +136527,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRBBUSD" + "symbol": "AGLDBUSD" }, { - "baseAsset": "TRB", + "baseAsset": "AGLD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -88374,9 +136548,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -88390,7 +136564,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4233.30723194", + "maxQty": "116694.87046560", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88424,18 +136598,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRBUSDT" + "symbol": "AGLDUSDT" }, { - "baseAsset": "ETH", + "baseAsset": "RAD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -88445,15 +136619,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "500.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -88461,7 +136635,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "126.74055013", + "maxQty": "7441.58019457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88488,24 +136662,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "NGN", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHNGN" + "symbol": "RADBTC" }, { - "baseAsset": "DOT", + "baseAsset": "RAD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "50000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -88515,15 +136689,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -88531,7 +136705,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5673.84604236", + "maxQty": "5102.10806115", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88558,22 +136732,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTBIDR" + "symbol": "RADBNB" }, { - "baseAsset": "LINK", + "baseAsset": "RAD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -88585,9 +136759,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -88601,7 +136775,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2550.72508681", + "maxQty": "7026.61987491", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88628,22 +136802,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "AUD", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKAUD" + "symbol": "RADBUSD" }, { - "baseAsset": "SXP", + "baseAsset": "RAD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -88655,9 +136829,79 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "27139.99339819", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "RADUSDT" + }, + { + "baseAsset": "UNI", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -88671,7 +136915,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12418.15820000", + "maxQty": "473.20695833", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88703,19 +136947,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "SXPAUD" + "status": "BREAK", + "symbol": "UNIAUD" }, { - "baseAsset": "BZRX", + "baseAsset": "HIVE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -88725,15 +136969,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -88741,7 +136985,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "32864.14458333", + "maxQty": "9997.04656011", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88755,7 +136999,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -88766,26 +137010,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BZRXBNB" + "status": "TRADING", + "symbol": "HIVEBUSD" }, { - "baseAsset": "BZRX", + "baseAsset": "STPT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -88795,15 +137040,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -88811,7 +137056,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "209937.64305556", + "maxQty": "42125.88123697", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88825,7 +137070,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -88836,26 +137081,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BZRXBTC" + "symbol": "STPTBUSD" }, { - "baseAsset": "BZRX", + "baseAsset": "BETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -88865,15 +137111,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -88881,7 +137127,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "84837.85671528", + "maxQty": "170011.95552466", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88908,24 +137154,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BZRXBUSD" + "symbol": "BETABTC" }, { - "baseAsset": "BZRX", + "baseAsset": "BETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -88935,15 +137181,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -88951,7 +137197,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "307372.35252083", + "maxQty": "119686.85128561", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -88978,22 +137224,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BZRXUSDT" + "symbol": "BETABNB" }, { - "baseAsset": "WBTC", + "baseAsset": "BETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00001000", "tickSize": "0.00001000" }, @@ -89005,15 +137251,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -89021,7 +137267,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "243.06569722", + "maxQty": "119230.81028492", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89048,24 +137294,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WBTCBTC" + "symbol": "BETABUSD" }, { - "baseAsset": "WBTC", + "baseAsset": "BETA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -89075,15 +137321,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -89091,7 +137337,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3.70762174", + "maxQty": "459113.30090340", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89105,7 +137351,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89116,26 +137362,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WBTCETH" + "symbol": "BETAUSDT" }, { - "baseAsset": "SUSHI", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -89145,15 +137392,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -89161,9 +137408,9 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3360.52679861", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "maxQty": "3777087663.40", + "minQty": "0.00", + "stepSize": "0.00" }, { "filterType": "MAX_NUM_ORDERS", @@ -89188,24 +137435,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSHIBNB" + "symbol": "SHIBAUD" }, { - "baseAsset": "SUSHI", + "baseAsset": "RARE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -89215,9 +137462,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -89231,7 +137478,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "66384.90646528", + "maxQty": "41325.67234190", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89245,7 +137492,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89256,8 +137503,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -89265,18 +137511,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSHIBTC" + "symbol": "RAREBTC" }, { - "baseAsset": "SUSHI", + "baseAsset": "RARE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -89286,15 +137532,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -89302,7 +137548,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "41142.45309514", + "maxQty": "17329.90500347", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89329,22 +137575,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSHIBUSD" + "symbol": "RAREBNB" }, { - "baseAsset": "SUSHI", + "baseAsset": "RARE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -89356,9 +137602,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -89372,7 +137618,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "155987.95086806", + "maxQty": "44308.57574704", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89386,7 +137632,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89397,27 +137643,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSHIUSDT" + "symbol": "RAREBUSD" }, { - "baseAsset": "YFII", + "baseAsset": "RARE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -89427,15 +137672,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -89443,7 +137688,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5.47484278", + "maxQty": "199115.19937456", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89470,24 +137715,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIIBNB" + "symbol": "RAREUSDT" }, { - "baseAsset": "YFII", + "baseAsset": "AVAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -89497,15 +137742,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -89513,7 +137758,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63.95452118", + "maxQty": "273.07350243", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89527,7 +137772,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89538,25 +137783,24 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIIBTC" + "symbol": "AVAXBRL" }, { - "baseAsset": "YFII", + "baseAsset": "AVAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -89568,9 +137812,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -89584,7 +137828,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26.52677261", + "maxQty": "545.04006254", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89611,22 +137855,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIIBUSD" + "symbol": "AVAXAUD" }, { - "baseAsset": "YFII", + "baseAsset": "LUNA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -89638,9 +137882,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -89654,7 +137898,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "219.61315473", + "maxQty": "825.33760250", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89668,7 +137912,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89679,27 +137923,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIIUSDT" + "symbol": "LUNAAUD" }, { - "baseAsset": "KSM", + "baseAsset": "TROY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -89709,15 +137952,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -89725,7 +137968,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "176.51163889", + "maxQty": "1730973.38429464", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89752,24 +137995,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KSMBNB" + "symbol": "TROYBUSD" }, { - "baseAsset": "KSM", + "baseAsset": "AXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -89779,7 +138022,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -89787,7 +138030,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -89795,7 +138038,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1439.32087361", + "maxQty": "894.74255733", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89809,7 +138052,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89820,27 +138063,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KSMBTC" + "symbol": "AXSETH" }, { - "baseAsset": "KSM", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -89850,15 +138092,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -89866,7 +138108,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "920.34213611", + "maxQty": "78825.50590687", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89893,24 +138135,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KSMBUSD" + "symbol": "FTMETH" }, { - "baseAsset": "KSM", + "baseAsset": "SOL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -89920,7 +138162,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -89928,7 +138170,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -89936,7 +138178,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2570.95059583", + "maxQty": "4837.30360180", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -89950,7 +138192,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -89961,27 +138203,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KSMUSDT" + "symbol": "SOLETH" }, { - "baseAsset": "EGLD", + "baseAsset": "SSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -89991,7 +138232,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -89999,7 +138240,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -90007,7 +138248,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "716.47535417", + "maxQty": "3143.76453092", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90034,16 +138275,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EGLDBNB" + "symbol": "SSVBTC" }, { - "baseAsset": "EGLD", + "baseAsset": "SSV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -90061,15 +138302,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -90077,7 +138318,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4813.93808194", + "maxQty": "1983.28526059", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90104,22 +138345,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EGLDBTC" + "symbol": "SSVETH" }, { - "baseAsset": "EGLD", + "baseAsset": "LAZIO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -90131,9 +138372,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -90147,7 +138388,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5179.41079931", + "maxQty": "16901.34784572", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90174,24 +138415,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EGLDBUSD" + "symbol": "LAZIOTRY" }, { - "baseAsset": "EGLD", + "baseAsset": "LAZIO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -90201,9 +138442,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -90217,7 +138458,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17586.17319931", + "maxQty": "3093.59026407", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90244,24 +138485,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "EUR", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EGLDUSDT" + "symbol": "LAZIOEUR" }, { - "baseAsset": "DIA", + "baseAsset": "LAZIO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -90271,7 +138512,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -90279,7 +138520,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -90287,7 +138528,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6944.51725694", + "maxQty": "4018.66435024", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90314,24 +138555,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DIABNB" + "symbol": "LAZIOBTC" }, { - "baseAsset": "DIA", + "baseAsset": "LAZIO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -90341,7 +138582,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -90349,7 +138590,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -90357,7 +138598,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "41548.86606944", + "maxQty": "19975.42911744", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90371,7 +138612,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90382,27 +138623,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DIABTC" + "symbol": "LAZIOUSDT" }, { - "baseAsset": "DIA", + "baseAsset": "CHESS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -90412,15 +138652,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -90428,7 +138668,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10612.95660278", + "maxQty": "23579.35649756", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90442,7 +138682,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90453,26 +138693,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DIABUSD" + "symbol": "CHESSBTC" }, { - "baseAsset": "DIA", + "baseAsset": "CHESS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -90482,15 +138723,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -90498,7 +138739,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "84628.50515694", + "maxQty": "12715.66289089", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90512,7 +138753,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90523,27 +138764,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DIAUSDT" + "symbol": "CHESSBNB" }, { - "baseAsset": "RUNE", + "baseAsset": "CHESS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -90553,9 +138793,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -90569,7 +138809,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "116110.22973611", + "maxQty": "26621.48630993", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90583,7 +138823,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90594,26 +138834,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RUNEUSDT" + "symbol": "CHESSBUSD" }, { - "baseAsset": "FIO", + "baseAsset": "CHESS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -90623,9 +138864,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -90639,7 +138880,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "702423.92459028", + "maxQty": "98643.19812369", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90653,7 +138894,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90664,7 +138905,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -90672,18 +138914,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FIOUSDT" + "symbol": "CHESSUSDT" }, { - "baseAsset": "UMA", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -90693,15 +138935,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -90709,7 +138951,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2520.89030694", + "maxQty": "34954.77484364", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90723,7 +138965,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90734,27 +138976,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UMABTC" + "symbol": "FTMAUD" }, { - "baseAsset": "UMA", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -90764,9 +139005,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -90780,7 +139021,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3908.38666181", + "maxQty": "9295.50667129", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90794,7 +139035,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -90805,26 +139046,25 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UMAUSDT" + "symbol": "FTMBRL" }, { - "baseAsset": "EOSUP", + "baseAsset": "SCRT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "3.53700000", - "minPrice": "0.18700000", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", "tickSize": "0.00100000" }, { @@ -90835,9 +139075,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -90851,7 +139091,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "117829.97311111", + "maxQty": "31931.34482279", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90866,7 +139106,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -90876,26 +139116,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EOSUPUSDT" + "symbol": "SCRTBUSD" }, { - "baseAsset": "EOSDOWN", + "baseAsset": "ADX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "5.54200000", - "minPrice": "0.29200000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -90905,9 +139145,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -90921,7 +139161,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "75959.61451389", + "maxQty": "161563.41834607", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -90936,7 +139176,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -90946,7 +139186,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -90954,18 +139194,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EOSDOWNUSDT" + "symbol": "ADXUSDT" }, { - "baseAsset": "TRXUP", + "baseAsset": "AUCTION", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "4.36600000", - "minPrice": "0.23000000", - "tickSize": "0.00100000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -90975,7 +139215,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -90991,7 +139231,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "90378.76757639", + "maxQty": "5072.43238359", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91006,7 +139246,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -91016,7 +139256,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -91024,17 +139264,17 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRXUPUSDT" + "symbol": "AUCTIONUSDT" }, { - "baseAsset": "TRXDOWN", + "baseAsset": "CELO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "4.45300000", - "minPrice": "0.23500000", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", "tickSize": "0.00100000" }, { @@ -91045,9 +139285,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -91061,7 +139301,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "86747.69009028", + "maxQty": "16138.98540653", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91075,8 +139315,8 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -91086,26 +139326,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRXDOWNUSDT" + "symbol": "CELOBUSD" }, { - "baseAsset": "XRPUP", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "3.15000000", - "minPrice": "0.16600000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -91115,15 +139356,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -91131,7 +139372,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "176670.86418750", + "maxQty": "7003.85198054", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91146,7 +139387,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -91156,25 +139397,25 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPUPUSDT" + "symbol": "FTMRUB" }, { - "baseAsset": "XRPDOWN", + "baseAsset": "NU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.35130000", - "minPrice": "0.01850000", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", "tickSize": "0.00010000" }, { @@ -91185,9 +139426,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -91201,7 +139442,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1258567.14405556", + "maxQty": "37269.66388888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91216,7 +139457,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -91226,26 +139467,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "XRPDOWNUSDT" + "status": "BREAK", + "symbol": "NUAUD" }, { - "baseAsset": "DOTUP", + "baseAsset": "NU", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "84.99300000", - "minPrice": "4.47400000", - "tickSize": "0.00100000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -91255,15 +139496,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -91271,7 +139512,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5992.23730556", + "maxQty": "14792.86138888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91286,7 +139527,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -91296,26 +139537,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "DOTUPUSDT" + "status": "BREAK", + "symbol": "NURUB" }, { - "baseAsset": "DOTDOWN", + "baseAsset": "REEF", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.03517000", - "minPrice": "0.00186000", - "tickSize": "0.00001000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -91325,9 +139566,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -91341,7 +139582,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9098512.53673611", + "maxQty": "2011835.58651841", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91356,7 +139597,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -91366,26 +139607,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTDOWNUSDT" + "symbol": "REEFTRY" }, { - "baseAsset": "SRM", + "baseAsset": "REEF", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "50000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "100000.00", + "minPrice": "0.10", + "tickSize": "0.10" }, { "avgPriceMins": 5, @@ -91395,9 +139636,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -91411,7 +139652,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6981.92430764", + "maxQty": "534729.70813064", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91444,18 +139685,89 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 2, "status": "TRADING", - "symbol": "SRMBIDR" + "symbol": "REEFBIDR" }, { - "baseAsset": "ONE", + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "46116860414.00", + "minQty": "1.00", + "stepSize": "1.00" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "30.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "9276225449.15", + "minQty": "0.00", + "stepSize": "0.00" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": true, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT", + "MARGIN" + ], + "quoteAsset": "DOGE", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SHIBDOGE" + }, + { + "baseAsset": "DAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00", - "minPrice": "0.01", - "tickSize": "0.01" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -91465,7 +139777,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -91473,7 +139785,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -91481,7 +139793,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "535632.50763889", + "maxQty": "257848.83947185", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91495,7 +139807,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -91506,26 +139818,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "ONEBIDR" + "symbol": "DARUSDT" }, { - "baseAsset": "LINK", + "baseAsset": "DAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -91535,9 +139848,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -91551,7 +139864,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4237.79398611", + "maxQty": "71374.43641417", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91565,7 +139878,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -91576,26 +139889,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "TRY", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKTRY" + "symbol": "DARBUSD" }, { - "baseAsset": "USDT", + "baseAsset": "DAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -91605,15 +139919,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "922320.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "500.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -91621,7 +139935,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "609913.17336111", + "maxQty": "50212.67129951", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91648,24 +139962,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "NGN", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDTNGN" + "symbol": "DARBNB" }, { - "baseAsset": "BEL", + "baseAsset": "DAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -91675,15 +139989,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -91691,7 +140005,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5657.85152778", + "maxQty": "34327.38846421", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91705,7 +140019,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -91716,26 +140030,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BELBNB" + "symbol": "DARBTC" }, { - "baseAsset": "BEL", + "baseAsset": "BNX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -91745,9 +140060,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -91761,7 +140076,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16720.55972222", + "maxQty": "441.42941209", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91775,7 +140090,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -91786,7 +140101,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -91794,10 +140110,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BELBTC" + "symbol": "BNXBTC" }, { - "baseAsset": "BEL", + "baseAsset": "BNX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -91815,15 +140131,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -91831,7 +140147,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8888.36802778", + "maxQty": "538.81544544", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91858,24 +140174,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BELBUSD" + "symbol": "BNXBNB" }, { - "baseAsset": "BEL", + "baseAsset": "BNX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -91885,9 +140201,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -91901,77 +140217,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "61375.95273611", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "BELUSDT" - }, - { - "baseAsset": "WING", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "654.16325694", + "maxQty": "882.72358234", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -91985,7 +140231,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -91996,26 +140242,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINGBNB" + "symbol": "BNXBUSD" }, { - "baseAsset": "WING", + "baseAsset": "BNX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -92025,7 +140272,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "922327.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -92033,7 +140280,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -92041,7 +140288,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2491.42762847", + "maxQty": "5093.63257539", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92055,7 +140302,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -92066,26 +140313,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINGBTC" + "symbol": "BNXUSDT" }, { - "baseAsset": "SWRV", + "baseAsset": "RGT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -92095,80 +140343,10 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "922327.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "10086.97051389", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "SWRVBNB" - }, - { - "baseAsset": "SWRV", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" - }, { "applyToMarket": true, "avgPriceMins": 5, @@ -92181,7 +140359,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12697.37249097", + "maxQty": "3780.53547602", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92208,24 +140386,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SWRVBUSD" + "symbol": "RGTUSDT" }, { - "baseAsset": "WING", + "baseAsset": "RGT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -92235,15 +140413,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -92251,7 +140429,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "600.00709306", + "maxQty": "412.72132036", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92278,24 +140456,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINGBUSD" + "symbol": "RGTBTC" }, { - "baseAsset": "WING", + "baseAsset": "RGT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -92305,9 +140483,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "922327.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -92321,7 +140499,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5718.52187292", + "maxQty": "814.97975677", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92348,24 +140526,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "WINGUSDT" + "symbol": "RGTBUSD" }, { - "baseAsset": "LTCUP", + "baseAsset": "RGT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "43.17300000", - "minPrice": "2.27300000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -92375,7 +140553,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -92383,7 +140561,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -92391,7 +140569,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10978.96961806", + "maxQty": "479.11774843", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92406,7 +140584,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -92416,26 +140594,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LTCUPUSDT" + "symbol": "RGTBNB" }, { - "baseAsset": "LTCDOWN", + "baseAsset": "LAZIO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.11875000", - "minPrice": "0.00626000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -92445,7 +140623,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "922327.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -92461,7 +140639,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3757034.73918056", + "maxQty": "3839.66073662", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92476,7 +140654,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -92486,26 +140664,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LTCDOWNUSDT" + "symbol": "LAZIOBUSD" }, { - "baseAsset": "LEND", + "baseAsset": "OXT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -92515,15 +140693,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922327.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -92531,7 +140709,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "143046.92375000", + "maxQty": "53795.53092425", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92558,24 +140736,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "LENDBKRW" + "status": "TRADING", + "symbol": "OXTBUSD" }, { - "baseAsset": "SXP", + "baseAsset": "MANA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -92585,9 +140763,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -92601,7 +140779,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "52719.21329097", + "maxQty": "58945.82237665", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92628,24 +140806,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPEUR" + "symbol": "MANATRY" }, { - "baseAsset": "CREAM", + "baseAsset": "ALGO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -92655,15 +140833,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -92671,7 +140849,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "90.54842361", + "maxQty": "10993.23919388", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92698,24 +140876,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CREAMBNB" + "symbol": "ALGORUB" }, { - "baseAsset": "CREAM", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, + "baseAsset": "SHIB", + "baseAssetPrecision": 2, + "baseCommissionPrecision": 2, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -92725,15 +140903,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00", + "minQty": "1.00", + "stepSize": "1.00" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -92741,9 +140919,9 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "550.43632986", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "maxQty": "92141578.00", + "minQty": "0.00", + "stepSize": "0.00" }, { "filterType": "MAX_NUM_ORDERS", @@ -92768,24 +140946,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "UAH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CREAMBUSD" + "symbol": "SHIBUAH" }, { - "baseAsset": "UNI", + "baseAsset": "LUNA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -92795,15 +140973,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922337194.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -92811,7 +140989,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4780.96826389", + "maxQty": "569.52940236", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92838,24 +141016,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "UNIBNB" + "symbol": "LUNABIDR" }, { - "baseAsset": "UNI", + "baseAsset": "AUD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -92865,7 +141043,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "922327.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -92873,7 +141051,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -92881,7 +141059,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "46269.04097222", + "maxQty": "281189.46143154", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92895,7 +141073,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -92906,27 +141084,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNIBTC" + "symbol": "AUDUSDC" }, { - "baseAsset": "UNI", + "baseAsset": "MOVR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -92936,15 +141113,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -92952,7 +141129,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "31450.84727083", + "maxQty": "504.32267338", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -92966,7 +141143,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -92977,19 +141154,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNIBUSD" + "symbol": "MOVRBTC" }, { - "baseAsset": "UNI", + "baseAsset": "MOVR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -93007,15 +141183,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -93023,7 +141199,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "111772.26227083", + "maxQty": "229.02374496", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93037,7 +141213,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -93048,27 +141224,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNIUSDT" + "symbol": "MOVRBNB" }, { - "baseAsset": "NBS", + "baseAsset": "MOVR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -93078,15 +141253,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -93094,7 +141269,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "9286028.95555556", + "maxQty": "244.37897220", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93121,24 +141296,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NBSBTC" + "symbol": "MOVRBUSD" }, { - "baseAsset": "NBS", + "baseAsset": "MOVR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -93148,9 +141323,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -93164,7 +141339,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2885666.72333333", + "maxQty": "1841.87413620", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93197,18 +141372,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NBSUSDT" + "symbol": "MOVRUSDT" }, { - "baseAsset": "OXT", + "baseAsset": "CITY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -93218,9 +141393,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -93234,7 +141409,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "241957.01597222", + "maxQty": "1889.98505906", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93267,18 +141442,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OXTBTC" + "symbol": "CITYBTC" }, { - "baseAsset": "OXT", + "baseAsset": "CITY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -93288,7 +141463,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -93296,7 +141471,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -93304,7 +141479,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "259949.12153472", + "maxQty": "2103.11251563", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93331,24 +141506,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OXTUSDT" + "symbol": "CITYBNB" }, { - "baseAsset": "SUN", + "baseAsset": "CITY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -93358,15 +141533,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -93374,7 +141549,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3480.14527431", + "maxQty": "1406.63486448", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93401,24 +141576,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUNBTC" + "symbol": "CITYBUSD" }, { - "baseAsset": "SUN", + "baseAsset": "CITY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -93428,9 +141603,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -93444,7 +141619,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4265.05091736", + "maxQty": "7938.47940236", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93477,18 +141652,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUNUSDT" + "symbol": "CITYUSDT" }, { - "baseAsset": "AVAX", + "baseAsset": "ENS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -93498,15 +141673,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -93514,7 +141689,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2895.77402778", + "maxQty": "3525.78856845", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93528,7 +141703,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -93539,26 +141714,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVAXBNB" + "symbol": "ENSBTC" }, { - "baseAsset": "AVAX", + "baseAsset": "ENS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -93568,15 +141744,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -93584,7 +141760,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23653.96861111", + "maxQty": "2517.10513551", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93598,7 +141774,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -93609,27 +141785,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVAXBTC" + "symbol": "ENSBNB" }, { - "baseAsset": "AVAX", + "baseAsset": "ENS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -93639,7 +141814,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -93655,7 +141830,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10617.81258333", + "maxQty": "4124.79462126", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93669,7 +141844,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -93680,7 +141855,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -93688,18 +141864,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVAXBUSD" + "symbol": "ENSBUSD" }, { - "baseAsset": "AVAX", + "baseAsset": "ENS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -93709,7 +141885,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -93725,7 +141901,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "47703.63992361", + "maxQty": "27480.29293259", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93759,10 +141935,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVAXUSDT" + "symbol": "ENSUSDT" }, { - "baseAsset": "HNT", + "baseAsset": "SAND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -93780,15 +141956,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -93796,7 +141972,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12635.00020139", + "maxQty": "33775.38380820", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93823,24 +141999,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HNTBTC" + "symbol": "SANDETH" }, { - "baseAsset": "HNT", + "baseAsset": "DOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -93850,15 +142026,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -93866,7 +142042,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "26981.41017361", + "maxQty": "12876.63482974", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93893,24 +142069,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HNTUSDT" + "symbol": "DOTETH" }, { - "baseAsset": "BAKE", + "baseAsset": "MATIC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -93920,15 +142096,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -93936,7 +142112,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "455681.33194444", + "maxQty": "94852.25337039", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -93963,16 +142139,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BAKEBNB" + "symbol": "MATICETH" }, { - "baseAsset": "BURGER", + "baseAsset": "ANKR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -93990,7 +142166,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -93998,7 +142174,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94006,7 +142182,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "20812.35666667", + "maxQty": "759968.16191799", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94020,7 +142196,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -94031,26 +142207,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BURGERBNB" + "symbol": "ANKRBUSD" }, { - "baseAsset": "SXP", + "baseAsset": "SAND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -94060,15 +142237,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94076,7 +142253,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8763.15400694", + "maxQty": "32035.20555941", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94103,22 +142280,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPBIDR" + "symbol": "SANDTRY" }, { - "baseAsset": "LINK", + "baseAsset": "MANA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -94130,7 +142307,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", + "maxQty": "9222449.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -94138,7 +142315,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94146,7 +142323,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "709.81472222", + "maxQty": "5518.93773453", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94173,24 +142350,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "LINKBKRW" + "status": "TRADING", + "symbol": "MANABRL" }, { - "baseAsset": "FLM", + "baseAsset": "KP3R", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "999990.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -94200,15 +142377,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92230.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94216,7 +142393,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "24574.78335645", + "maxQty": "248.20868658", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94243,24 +142420,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "FLMBNB" + "status": "TRADING", + "symbol": "KP3RUSDT" }, { - "baseAsset": "FLM", + "baseAsset": "QI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -94270,7 +142447,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -94278,7 +142455,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94286,7 +142463,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "232438.57500000", + "maxQty": "1850522.36275191", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94313,16 +142490,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FLMBTC" + "symbol": "QIUSDT" }, { - "baseAsset": "FLM", + "baseAsset": "QI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -94340,9 +142517,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -94356,7 +142533,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43720.26578472", + "maxQty": "378297.48853370", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94389,18 +142566,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FLMBUSD" + "symbol": "QIBUSD" }, { - "baseAsset": "FLM", + "baseAsset": "QI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -94410,15 +142587,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -94426,7 +142603,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "467183.58813889", + "maxQty": "379295.94023627", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94453,16 +142630,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FLMUSDT" + "symbol": "QIBNB" }, { - "baseAsset": "SCRT", + "baseAsset": "QI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -94480,7 +142657,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -94496,7 +142673,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "43080.35347222", + "maxQty": "452667.22932592", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94529,18 +142706,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SCRTBTC" + "symbol": "QIBTC" }, { - "baseAsset": "SCRT", + "baseAsset": "PORTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -94550,7 +142727,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -94558,7 +142735,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -94566,7 +142743,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23161.03027778", + "maxQty": "19407.37013898", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94593,24 +142770,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SCRTETH" + "symbol": "PORTOBTC" }, { - "baseAsset": "CAKE", + "baseAsset": "PORTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -94620,15 +142797,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94636,7 +142813,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25518.81909722", + "maxQty": "29654.33667824", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94663,24 +142840,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CAKEBNB" + "symbol": "PORTOUSDT" }, { - "baseAsset": "CAKE", + "baseAsset": "PORTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -94690,7 +142867,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -94706,7 +142883,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "53396.02040972", + "maxQty": "9325.90869353", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94733,24 +142910,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CAKEBUSD" + "symbol": "PORTOTRY" }, { - "baseAsset": "SPARTA", + "baseAsset": "PORTO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -94760,15 +142937,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -94776,7 +142953,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "144895.00833333", + "maxQty": "17309.15626824", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94803,24 +142980,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "EUR", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SPARTABNB" + "symbol": "PORTOEUR" }, { - "baseAsset": "UNIUP", + "baseAsset": "POWR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "32.47000000", - "minPrice": "1.70900000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -94830,9 +143007,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -94846,7 +143023,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18897.85761111", + "maxQty": "291228.51911049", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94861,7 +143038,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -94871,7 +143048,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -94879,17 +143056,17 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNIUPUSDT" + "symbol": "POWRUSDT" }, { - "baseAsset": "UNIDOWN", + "baseAsset": "POWR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.10300000", - "minPrice": "0.00550000", + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", "tickSize": "0.00010000" }, { @@ -94900,9 +143077,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -94916,7 +143093,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3471000.87096528", + "maxQty": "115877.69284225", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -94931,7 +143108,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -94941,26 +143118,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNIDOWNUSDT" + "symbol": "POWRBUSD" }, { - "baseAsset": "ORN", + "baseAsset": "AVAX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -94970,7 +143147,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -94978,7 +143155,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -94986,7 +143163,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10775.43781944", + "maxQty": "6823.00008339", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95000,7 +143177,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -95011,19 +143188,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ORNBTC" + "symbol": "AVAXETH" }, { - "baseAsset": "ORN", + "baseAsset": "SLP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -95041,9 +143217,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -95057,7 +143233,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16250.25219444", + "maxQty": "14399273.50590687", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95071,7 +143247,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -95082,25 +143258,24 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ORNUSDT" + "symbol": "SLPTRY" }, { - "baseAsset": "TRX", + "baseAsset": "FIS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -95112,15 +143287,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "922320.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "500.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -95128,7 +143303,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "880422.56627083", + "maxQty": "8263.01591382", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95155,22 +143330,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "NGN", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRXNGN" + "symbol": "FISTRY" }, { - "baseAsset": "SXP", + "baseAsset": "LRC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.01000000", "tickSize": "0.01000000" }, @@ -95182,9 +143357,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -95198,7 +143373,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "36851.62418056", + "maxQty": "83266.68936761", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95231,160 +143406,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPTRY" - }, - { - "baseAsset": "UTK", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "274131.72638889", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "UTKBTC" + "symbol": "LRCTRY" }, { - "baseAsset": "UTK", + "baseAsset": "CHR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "277313.36073611", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": true, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT", - "MARGIN" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "UTKUSDT" - }, - { - "baseAsset": "XVS", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -95394,15 +143427,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -95410,7 +143443,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1505.54507639", + "maxQty": "67552.25767894", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95437,24 +143470,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XVSBNB" + "symbol": "CHRETH" }, { - "baseAsset": "XVS", + "baseAsset": "FIS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "10000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -95464,15 +143497,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9223371114.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -95480,7 +143513,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7258.79446528", + "maxQty": "14356.12967338", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95507,16 +143540,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "XVSBTC" + "symbol": "FISBIDR" }, { - "baseAsset": "XVS", + "baseAsset": "VGX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -95534,15 +143567,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -95550,7 +143583,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1709.38172153", + "maxQty": "40116.47359277", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95577,24 +143610,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XVSBUSD" + "symbol": "VGXUSDT" }, { - "baseAsset": "XVS", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -95604,15 +143637,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -95620,7 +143653,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15048.68744583", + "maxQty": "179037.77414871", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95647,24 +143680,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XVSUSDT" + "symbol": "GALAETH" }, { - "baseAsset": "ALPHA", + "baseAsset": "JASMY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -95674,15 +143707,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -95690,7 +143723,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "24711.89513889", + "maxQty": "17169463.15587213", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95704,7 +143737,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -95715,26 +143748,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ALPHABNB" + "symbol": "JASMYUSDT" }, { - "baseAsset": "ALPHA", + "baseAsset": "JASMY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -95744,15 +143778,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -95760,7 +143794,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "177439.14652778", + "maxQty": "2916339.01028492", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95774,7 +143808,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -95785,26 +143819,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ALPHABTC" + "symbol": "JASMYBUSD" }, { - "baseAsset": "ALPHA", + "baseAsset": "JASMY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -95814,7 +143849,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -95822,7 +143857,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -95830,7 +143865,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "72201.32798611", + "maxQty": "2010843.97213342", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95857,24 +143892,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ALPHABUSD" + "symbol": "JASMYBNB" }, { - "baseAsset": "ALPHA", + "baseAsset": "JASMY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -95884,7 +143919,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -95892,7 +143927,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -95900,7 +143935,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "389480.35979167", + "maxQty": "11950076.94815844", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -95914,7 +143949,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -95925,18 +143960,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ALPHAUSDT" + "symbol": "JASMYBTC" }, { - "baseAsset": "VIDT", + "baseAsset": "AMP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -95954,7 +143990,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -95970,7 +144006,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "113361.66736111", + "maxQty": "2928023.34329395", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96003,18 +144039,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "VIDTBTC" + "symbol": "AMPBTC" }, { - "baseAsset": "VIDT", + "baseAsset": "AMP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -96024,15 +144060,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -96040,7 +144076,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "51661.98186111", + "maxQty": "844492.89089645", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96067,24 +144103,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "VIDTBUSD" + "symbol": "AMPBNB" }, { - "baseAsset": "AAVE", + "baseAsset": "AMP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -96094,15 +144130,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -96110,7 +144146,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "85.59408333", + "maxQty": "1380486.37109103", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96137,24 +144173,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEBNB" + "symbol": "AMPBUSD" }, { - "baseAsset": "BTC", + "baseAsset": "AMP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -96164,9 +144200,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -96180,7 +144216,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17.77521274", + "maxQty": "3095084.11188325", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96207,24 +144243,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCBRL" + "symbol": "AMPUSDT" }, { - "baseAsset": "USDT", + "baseAsset": "PLA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -96234,7 +144270,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -96242,7 +144278,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -96250,7 +144286,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "379833.66990278", + "maxQty": "29900.36537873", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96277,22 +144313,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDTBRL" + "symbol": "PLABTC" }, { - "baseAsset": "AAVE", + "baseAsset": "PLA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000100", "tickSize": "0.00000100" }, @@ -96304,7 +144340,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -96312,7 +144348,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -96320,7 +144356,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2292.85768056", + "maxQty": "29623.98398888", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96334,7 +144370,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -96345,27 +144381,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEBTC" + "symbol": "PLABNB" }, { - "baseAsset": "AAVE", + "baseAsset": "PLA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -96375,15 +144410,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -96391,7 +144426,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "443.74661319", + "maxQty": "28509.51936761", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96418,22 +144453,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEETH" + "symbol": "PLABUSD" }, { - "baseAsset": "AAVE", + "baseAsset": "PLA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -96445,9 +144480,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -96461,7 +144496,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "706.06305903", + "maxQty": "81599.55677553", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96488,24 +144523,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEBUSD" + "symbol": "PLAUSDT" }, { - "baseAsset": "AAVE", + "baseAsset": "PYR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -96515,7 +144550,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -96523,7 +144558,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -96531,7 +144566,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4521.36575486", + "maxQty": "10967.06710215", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96559,24 +144594,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEUSDT" + "symbol": "PYRBTC" }, { - "baseAsset": "AAVE", + "baseAsset": "PYR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "5000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -96586,7 +144621,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -96594,7 +144629,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -96602,7 +144637,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "129.78107222", + "maxQty": "7306.94603474", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96616,7 +144651,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -96627,26 +144662,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BKRW", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "AAVEBKRW" + "status": "TRADING", + "symbol": "PYRBUSD" }, { - "baseAsset": "NEAR", + "baseAsset": "PYR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -96656,15 +144692,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -96672,7 +144708,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "28556.81798611", + "maxQty": "29024.16786379", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96686,7 +144722,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -96697,18 +144733,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BNB", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEARBNB" + "symbol": "PYRUSDT" }, { - "baseAsset": "NEAR", + "baseAsset": "RNDR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -96726,7 +144763,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -96742,7 +144779,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "69753.80270833", + "maxQty": "37552.29513551", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96776,18 +144813,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEARBTC" + "symbol": "RNDRBTC" }, { - "baseAsset": "NEAR", + "baseAsset": "RNDR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -96797,7 +144834,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -96813,7 +144850,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "35673.23136806", + "maxQty": "112523.30933287", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96827,7 +144864,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -96838,26 +144875,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEARBUSD" + "symbol": "RNDRUSDT" }, { - "baseAsset": "NEAR", + "baseAsset": "RNDR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -96867,7 +144905,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -96883,7 +144921,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "130211.07690278", + "maxQty": "33485.67155663", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96911,24 +144949,24 @@ "SPOT", "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "NEARUSDT" + "symbol": "RNDRBUSD" }, { - "baseAsset": "SXPUP", + "baseAsset": "ALCX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1.25930000", - "minPrice": "0.06630000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -96938,9 +144976,79 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00010000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "168.25994148", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALCXBTC" + }, + { + "baseAsset": "ALCX", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "999996.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92233.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -96954,7 +145062,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "226685.60071528", + "maxQty": "77.26238464", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -96969,7 +145077,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -96979,26 +145087,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPUPUSDT" + "symbol": "ALCXBUSD" }, { - "baseAsset": "SXPDOWN", + "baseAsset": "ALCX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.01113000", - "minPrice": "0.00059000", - "tickSize": "0.00001000" + "maxPrice": "999996.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -97008,9 +145116,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92233.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, @@ -97024,7 +145132,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4344795.04126389", + "maxQty": "425.64596921", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97039,7 +145147,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -97049,7 +145157,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -97057,18 +145165,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPDOWNUSDT" + "symbol": "ALCXUSDT" }, { - "baseAsset": "DOT", + "baseAsset": "SANTOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -97078,15 +145186,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9200.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "1000.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -97094,7 +145202,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1469.73875000", + "maxQty": "6765.98059068", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97121,24 +145229,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BKRW", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "DOTBKRW" + "status": "TRADING", + "symbol": "SANTOSBTC" }, { - "baseAsset": "SXP", + "baseAsset": "SANTOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -97148,7 +145256,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -97164,7 +145272,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25763.02556250", + "maxQty": "24630.67201528", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97191,24 +145299,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "GBP", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SXPGBP" + "symbol": "SANTOSUSDT" }, { - "baseAsset": "FIL", + "baseAsset": "SANTOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -97218,15 +145326,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -97234,7 +145342,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5669.53840278", + "maxQty": "2142.59050729", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97261,16 +145369,86 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FILBNB" + "symbol": "SANTOSBRL" }, { - "baseAsset": "FIL", + "baseAsset": "SANTOS", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "5139.53011813", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "TRY", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "SANTOSTRY" + }, + { + "baseAsset": "MC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -97288,7 +145466,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -97304,7 +145482,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16751.03756250", + "maxQty": "15217.72813759", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97318,7 +145496,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -97329,8 +145507,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -97338,18 +145515,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FILBTC" + "symbol": "MCBTC" }, { - "baseAsset": "FIL", + "baseAsset": "MC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -97359,7 +145536,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -97375,7 +145552,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10085.13108333", + "maxQty": "9945.21865184", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97408,18 +145585,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FILBUSD" + "symbol": "MCBUSD" }, { - "baseAsset": "FIL", + "baseAsset": "MC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -97429,7 +145606,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -97445,7 +145622,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "60132.83406944", + "maxQty": "64802.42606671", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97459,7 +145636,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -97470,8 +145647,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -97479,18 +145655,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FILUSDT" + "symbol": "MCUSDT" }, { - "baseAsset": "FILUP", + "baseAsset": "BEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "7.96100000", - "minPrice": "0.42000000", - "tickSize": "0.00100000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -97500,7 +145676,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -97516,7 +145692,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "48295.26009028", + "maxQty": "13149.01759555", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97531,7 +145707,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -97541,26 +145717,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FILUPUSDT" + "symbol": "BELTRY" }, { - "baseAsset": "FILDOWN", + "baseAsset": "COCOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "4.46900000", - "minPrice": "0.23600000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -97570,9 +145746,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -97586,7 +145762,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "85568.87246528", + "maxQty": "22939.01737317", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97601,147 +145777,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "LEVERAGED" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "FILDOWNUSDT" - }, - { - "baseAsset": "YFIUP", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "15.40400000", - "minPrice": "0.81100000", - "tickSize": "0.00100000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "28845.37668056", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "LEVERAGED" - ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "YFIUPUSDT" - }, - { - "baseAsset": "YFIDOWN", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "0.06597000", - "minPrice": "0.00348000", - "tickSize": "0.00001000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "1243790.15637500", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -97751,18 +145787,18 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIDOWNUSDT" + "symbol": "COCOSBUSD" }, { - "baseAsset": "INJ", + "baseAsset": "DENT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -97780,15 +145816,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -97796,7 +145832,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2308.63125000", + "maxQty": "10387026.08269631", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97823,24 +145859,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "INJBNB" + "symbol": "DENTTRY" }, { - "baseAsset": "INJ", + "baseAsset": "ENJ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -97850,15 +145886,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -97866,7 +145902,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18560.35375000", + "maxQty": "17886.02773453", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97893,24 +145929,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "INJBTC" + "symbol": "ENJTRY" }, { - "baseAsset": "INJ", + "baseAsset": "NEO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "999996.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -97920,15 +145956,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92233.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -97936,7 +145972,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2857.76702778", + "maxQty": "297.72159068", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -97963,22 +145999,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "INJBUSD" + "symbol": "NEORUB" }, { - "baseAsset": "INJ", + "baseAsset": "SAND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.00010000", "tickSize": "0.00010000" }, @@ -97990,9 +146026,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -98006,7 +146042,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23702.70300000", + "maxQty": "7036.90757470", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98033,24 +146069,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "INJUSDT" + "symbol": "SANDAUD" }, { - "baseAsset": "AERGO", + "baseAsset": "SLP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00", + "minPrice": "0.10", + "tickSize": "0.10" }, { "avgPriceMins": 5, @@ -98060,7 +146096,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9223362229.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -98068,7 +146104,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -98076,7 +146112,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1199289.26597222", + "maxQty": "1927457.22654621", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98103,24 +146139,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "AERGOBTC" + "symbol": "SLPBIDR" }, { - "baseAsset": "AERGO", + "baseAsset": "ANY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -98130,15 +146166,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -98146,7 +146182,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "211937.93090278", + "maxQty": "3633.74120222", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98173,24 +146209,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AERGOBUSD" + "symbol": "ANYBTC" }, { - "baseAsset": "LINK", + "baseAsset": "ANY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -98200,9 +146236,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -98216,7 +146252,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15766.56785069", + "maxQty": "3094.50467685", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98243,24 +146279,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKEUR" + "symbol": "ANYBUSD" }, { - "baseAsset": "ONE", + "baseAsset": "ANY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -98270,9 +146306,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -98286,7 +146322,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6922170.74375000", + "maxQty": "7797.54639332", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98313,24 +146349,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ONEBUSD" + "symbol": "ANYUSDT" }, { - "baseAsset": "EASY", + "baseAsset": "BICO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -98340,80 +146376,10 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "9530.89346528", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "ETH", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "EASYETH" - }, - { - "baseAsset": "AUDIO", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, { "applyToMarket": true, "avgPriceMins": 5, @@ -98426,7 +146392,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "285156.08541667", + "maxQty": "31874.49191799", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98440,7 +146406,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -98451,7 +146417,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BTC", "quoteAssetPrecision": 8, @@ -98459,18 +146426,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AUDIOBTC" + "symbol": "BICOBTC" }, { - "baseAsset": "AUDIO", + "baseAsset": "BICO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -98480,9 +146447,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -98496,7 +146463,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "56502.00368056", + "maxQty": "32433.56000694", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98510,7 +146477,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -98521,7 +146488,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "BUSD", "quoteAssetPrecision": 8, @@ -98529,18 +146497,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AUDIOBUSD" + "symbol": "BICOBUSD" }, { - "baseAsset": "AUDIO", + "baseAsset": "BICO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -98550,9 +146518,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -98566,7 +146534,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "510023.19006944", + "maxQty": "119001.10870048", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98580,7 +146548,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -98591,7 +146559,8 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -98599,18 +146568,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AUDIOUSDT" + "symbol": "BICOUSDT" }, { - "baseAsset": "CTK", + "baseAsset": "FLUX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -98620,15 +146589,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -98636,7 +146605,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "10803.36166667", + "maxQty": "25344.91789437", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98663,24 +146632,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTKBNB" + "symbol": "FLUXBTC" }, { - "baseAsset": "CTK", + "baseAsset": "FLUX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -98690,15 +146659,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -98706,7 +146675,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "28927.70750000", + "maxQty": "30689.23109798", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98720,7 +146689,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -98731,26 +146700,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTKBTC" + "symbol": "FLUXBUSD" }, { - "baseAsset": "CTK", + "baseAsset": "FLUX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -98760,7 +146730,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -98776,7 +146746,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15739.57654861", + "maxQty": "70030.46106323", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98790,7 +146760,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -98801,26 +146771,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTKBUSD" + "symbol": "FLUXUSDT" }, { - "baseAsset": "CTK", + "baseAsset": "ALICE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -98830,9 +146801,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -98846,7 +146817,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "206820.07065972", + "maxQty": "3172.85685962", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98873,22 +146844,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CTKUSDT" + "symbol": "ALICETRY" }, { - "baseAsset": "BCHUP", + "baseAsset": "FXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "10000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -98900,9 +146871,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "9222449.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -98916,7 +146887,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "25795.58600278", + "maxQty": "4249.24940931", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -98931,7 +146902,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -98941,24 +146912,24 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCHUPUSDT" + "status": "TRADING", + "symbol": "FXSUSDT" }, { - "baseAsset": "BCHDOWN", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -98970,9 +146941,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -98986,7 +146957,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "27997.32476752", + "maxQty": "81233.34551772", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99001,7 +146972,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -99011,26 +146982,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "BREAK", - "symbol": "BCHDOWNUSDT" + "status": "TRADING", + "symbol": "GALABRL" }, { - "baseAsset": "BOT", + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -99040,15 +147011,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -99056,7 +147027,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "46.12847111", + "maxQty": "373529.85198054", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99083,24 +147054,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BOTBTC" + "symbol": "GALATRY" }, { - "baseAsset": "BOT", + "baseAsset": "LUNA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "999996.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -99110,9 +147081,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92233.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -99126,7 +147097,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "42.35176904", + "maxQty": "590.94026198", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99153,24 +147124,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BOTBUSD" + "symbol": "LUNATRY" }, { - "baseAsset": "ETH", + "baseAsset": "REQ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -99180,9 +147151,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "45000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -99196,7 +147167,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "180.63761725", + "maxQty": "36337.66990965", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99223,24 +147194,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ETHBRL" + "symbol": "REQBUSD" }, { - "baseAsset": "DOT", + "baseAsset": "SAND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -99250,9 +147221,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -99266,7 +147237,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "21528.80830347", + "maxQty": "3623.98468380", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99293,24 +147264,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DOTEUR" + "symbol": "SANDBRL" }, { - "baseAsset": "AKRO", + "baseAsset": "MANA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "10000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -99320,15 +147291,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9223371110.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -99336,7 +147307,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8889736.76805556", + "maxQty": "5320.62181375", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99350,7 +147321,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -99361,27 +147332,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "AKROBTC" + "symbol": "MANABIDR" }, { - "baseAsset": "AKRO", + "baseAsset": "SAND", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -99391,15 +147361,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "9223371110.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -99407,7 +147377,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3807841.75763889", + "maxQty": "2971.41729673", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99421,7 +147391,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -99432,27 +147402,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "AKROUSDT" + "symbol": "SANDBIDR" }, { - "baseAsset": "KP3R", + "baseAsset": "VOXEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -99462,15 +147431,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -99478,7 +147447,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63.21735000", + "maxQty": "57120.91806810", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99505,24 +147474,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KP3RBNB" + "symbol": "VOXELBTC" }, { - "baseAsset": "KP3R", + "baseAsset": "VOXEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -99532,15 +147501,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -99548,7 +147517,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "354.06113078", + "maxQty": "45309.34294649", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99575,24 +147544,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "KP3RBUSD" + "symbol": "VOXELBNB" }, { - "baseAsset": "AXS", + "baseAsset": "VOXEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -99602,15 +147571,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -99618,7 +147587,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18893.44166667", + "maxQty": "73559.43801250", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99645,24 +147614,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AXSBNB" + "symbol": "VOXELBUSD" }, { - "baseAsset": "AXS", + "baseAsset": "VOXEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -99672,15 +147641,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -99688,7 +147657,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "65382.40347222", + "maxQty": "262438.67421820", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99702,7 +147671,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -99713,18 +147682,19 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AXSBTC" + "symbol": "VOXELUSDT" }, { - "baseAsset": "AXS", + "baseAsset": "COS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -99742,7 +147712,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -99758,7 +147728,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "38025.18131944", + "maxQty": "871021.09958304", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99791,18 +147761,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AXSBUSD" + "symbol": "COSBUSD" }, { - "baseAsset": "AXS", + "baseAsset": "CTXC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -99812,9 +147782,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -99828,7 +147798,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "150065.25777778", + "maxQty": "70843.35858234", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99842,7 +147812,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -99853,26 +147823,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AXSUSDT" + "symbol": "CTXCBUSD" }, { - "baseAsset": "HARD", + "baseAsset": "FTM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -99882,15 +147853,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -99898,7 +147869,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "13365.85826389", + "maxQty": "29272.98318276", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99925,24 +147896,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HARDBNB" + "symbol": "FTMTRY" }, { - "baseAsset": "HARD", + "baseAsset": "MANA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -99952,15 +147923,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -99968,7 +147939,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "69807.30020833", + "maxQty": "20021.73337734", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -99995,24 +147966,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HARDBTC" + "symbol": "MANABNB" }, { - "baseAsset": "HARD", + "baseAsset": "MINA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -100022,7 +147993,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -100038,7 +148009,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "24965.45093750", + "maxQty": "14584.53340514", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100065,24 +148036,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HARDBUSD" + "symbol": "MINATRY" }, { - "baseAsset": "HARD", + "baseAsset": "XTZ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -100092,7 +148063,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -100108,7 +148079,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "196232.91450000", + "maxQty": "3584.91214732", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100135,24 +148106,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HARDUSDT" + "symbol": "XTZTRY" }, { - "baseAsset": "BNB", + "baseAsset": "HIGH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -100162,15 +148133,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "45000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -100178,7 +148149,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "891.28515273", + "maxQty": "6600.65633217", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100205,24 +148176,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BNBBRL" + "symbol": "HIGHBTC" }, { - "baseAsset": "LTC", + "baseAsset": "HIGH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -100232,7 +148203,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -100248,7 +148219,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2420.53363333", + "maxQty": "4774.11855524", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100275,24 +148246,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LTCEUR" + "symbol": "HIGHBUSD" }, { - "baseAsset": "RENBTC", + "baseAsset": "HIGH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -100302,15 +148273,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "9222449.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -100318,7 +148289,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "19.38218778", + "maxQty": "21609.41379569", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100345,24 +148316,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RENBTCBTC" + "symbol": "HIGHUSDT" }, { - "baseAsset": "RENBTC", + "baseAsset": "CVX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -100372,15 +148343,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -100388,7 +148359,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "0.31637479", + "maxQty": "1105.02621959", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100415,24 +148386,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RENBTCETH" + "symbol": "CVXBTC" }, { - "baseAsset": "DNT", + "baseAsset": "CVX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -100442,9 +148413,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -100458,7 +148429,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "95228.37548611", + "maxQty": "1152.39858304", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100491,18 +148462,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DNTBUSD" + "symbol": "CVXBUSD" }, { - "baseAsset": "DNT", + "baseAsset": "CVX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -100512,9 +148483,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "9222449.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -100528,7 +148499,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "411508.48222222", + "maxQty": "5272.58894718", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100542,7 +148513,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -100553,8 +148524,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "USDT", "quoteAssetPrecision": 8, @@ -100562,10 +148532,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DNTUSDT" + "symbol": "CVXUSDT" }, { - "baseAsset": "SLP", + "baseAsset": "PEOPLE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -100583,15 +148553,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -100599,7 +148569,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "972147.47500000", + "maxQty": "757541.16900625", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100613,7 +148583,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -100624,26 +148594,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SLPETH" + "symbol": "PEOPLEBTC" }, { - "baseAsset": "ADA", + "baseAsset": "PEOPLE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -100653,7 +148624,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -100669,77 +148640,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1471659.67618056", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "EUR", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "ADAEUR" - }, - { - "baseAsset": "LTC", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "900.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "500.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "99.45464498", + "maxQty": "1292051.89131341", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100753,7 +148654,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -100764,26 +148665,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "NGN", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LTCNGN" + "symbol": "PEOPLEBUSD" }, { - "baseAsset": "CVP", + "baseAsset": "PEOPLE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -100793,15 +148695,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -100809,7 +148711,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6897.48540278", + "maxQty": "8212698.51369006", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100823,7 +148725,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": false, + "isMarginTradingAllowed": true, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -100834,26 +148736,27 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT" + "SPOT", + "MARGIN" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CVPETH" + "symbol": "PEOPLEUSDT" }, { - "baseAsset": "CVP", + "baseAsset": "OOKI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -100863,9 +148766,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -100879,7 +148782,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7601.54109722", + "maxQty": "5098932.04100069", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100912,18 +148815,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CVPBUSD" + "symbol": "OOKIBUSD" }, { - "baseAsset": "STRAX", + "baseAsset": "OOKI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -100933,15 +148836,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -100949,7 +148852,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "81196.41368056", + "maxQty": "10825278.06462821", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -100976,24 +148879,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STRAXBTC" + "symbol": "OOKIUSDT" }, { - "baseAsset": "STRAX", + "baseAsset": "COCOS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -101003,7 +148906,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -101011,7 +148914,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -101019,7 +148922,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "35162.38044444", + "maxQty": "29007.77775538", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101046,24 +148949,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STRAXETH" + "symbol": "COCOSTRY" }, { - "baseAsset": "STRAX", + "baseAsset": "GXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -101073,7 +148976,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -101081,7 +148984,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -101089,7 +148992,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "27670.14850694", + "maxQty": "17845.21876997", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101116,24 +149019,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STRAXBUSD" + "symbol": "GXSBNB" }, { - "baseAsset": "STRAX", + "baseAsset": "LINK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -101143,15 +149046,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -101159,7 +149062,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "140663.41460417", + "maxQty": "2619.45643085", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101186,24 +149089,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "STRAXUSDT" + "symbol": "LINKBNB" }, { - "baseAsset": "FOR", + "baseAsset": "LUNA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -101213,15 +149116,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -101229,7 +149132,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2218560.39861111", + "maxQty": "4618.87655733", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101256,16 +149159,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FORBTC" + "symbol": "LUNAETH" }, { - "baseAsset": "FOR", + "baseAsset": "MDT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -101283,7 +149186,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -101299,7 +149202,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "487370.97833333", + "maxQty": "343402.55851285", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101332,18 +149235,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FORBUSD" + "symbol": "MDTBUSD" }, { - "baseAsset": "UNFI", + "baseAsset": "NULS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -101353,15 +149256,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -101369,7 +149272,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1335.37270833", + "maxQty": "31757.27032661", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101396,16 +149299,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNFIBNB" + "symbol": "NULSBUSD" }, { - "baseAsset": "UNFI", + "baseAsset": "SPELL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -101423,9 +149326,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -101439,7 +149342,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "7809.16840278", + "maxQty": "92141578.00000000", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101471,19 +149374,19 @@ "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, - "status": "TRADING", - "symbol": "UNFIBTC" + "status": "BREAK", + "symbol": "SPELLBTC" }, { - "baseAsset": "UNFI", + "baseAsset": "SPELL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -101493,9 +149396,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -101509,7 +149412,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2132.59393056", + "maxQty": "38789891.10910354", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101536,24 +149439,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNFIBUSD" + "symbol": "SPELLUSDT" }, { - "baseAsset": "UNFI", + "baseAsset": "SPELL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -101563,9 +149466,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -101579,7 +149482,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "18933.86268056", + "maxQty": "6790613.64211257", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101606,24 +149509,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "UNFIUSDT" + "symbol": "SPELLBUSD" }, { - "baseAsset": "FRONT", + "baseAsset": "UST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -101633,15 +149536,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -101649,7 +149552,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16705.71736111", + "maxQty": "153649.90075052", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101676,36 +149579,36 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FRONTETH" + "symbol": "USTBTC" }, { - "baseAsset": "FRONT", + "baseAsset": "UST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", + "maxPrice": "1.30000000", + "minPrice": "0.70000000", "tickSize": "0.00010000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -101719,7 +149622,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "27819.09750694", + "maxQty": "1998773.28978457", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101752,30 +149655,30 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "FRONTBUSD" + "symbol": "USTBUSD" }, { - "baseAsset": "BCHA", + "baseAsset": "UST", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1.30000000", + "minPrice": "0.70000000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" + "multiplierDown": "0.8", + "multiplierUp": "1.2" }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -101789,7 +149692,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "5642.73647639", + "maxQty": "2971574.40583738", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101816,16 +149719,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BCHABUSD" + "symbol": "USTUSDT" }, { - "baseAsset": "ROSE", + "baseAsset": "JOE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -101843,9 +149746,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -101859,7 +149762,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2253610.97986111", + "maxQty": "17699.60519110", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101892,18 +149795,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ROSEBTC" + "symbol": "JOEBTC" }, { - "baseAsset": "ROSE", + "baseAsset": "JOE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -101913,9 +149816,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -101929,7 +149832,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "822432.03465278", + "maxQty": "20406.30808895", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -101962,18 +149865,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ROSEBUSD" + "symbol": "JOEBUSD" }, { - "baseAsset": "ROSE", + "baseAsset": "JOE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -101983,9 +149886,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -101999,7 +149902,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3438128.98368056", + "maxQty": "103599.75977762", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102032,18 +149935,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ROSEUSDT" + "symbol": "JOEUSDT" }, { - "baseAsset": "AVAX", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -102053,15 +149956,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -102069,7 +149972,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15163.64991667", + "maxQty": "4628.28282279", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102096,24 +149999,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TRY", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVAXTRY" + "symbol": "ATOMETH" }, { - "baseAsset": "BUSD", + "baseAsset": "DUSK", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -102123,9 +150026,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -102139,7 +150042,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "130239.43555556", + "maxQty": "86037.51494093", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102166,24 +150069,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BUSDBRL" + "symbol": "DUSKBUSD" }, { - "baseAsset": "AVA", + "baseAsset": "EGLD", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -102193,15 +150096,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -102209,7 +150112,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "46282.18156944", + "maxQty": "291.83543314", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102236,24 +150139,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AVAUSDT" + "symbol": "EGLDETH" }, { - "baseAsset": "SYS", + "baseAsset": "ICP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -102263,9 +150166,79 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.00500000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "1456.05648992", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "ETH", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ICPETH" + }, + { + "baseAsset": "LUNA", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -102279,7 +150252,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "149155.62625000", + "maxQty": "272.43014315", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102306,24 +150279,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SYSBUSD" + "symbol": "LUNABRL" }, { - "baseAsset": "XEM", + "baseAsset": "LUNA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "10000.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -102333,7 +150306,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "9222449.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -102349,7 +150322,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2189145.32811806", + "maxQty": "5033.66216817", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102376,24 +150349,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "UST", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XEMUSDT" + "symbol": "LUNAUST" }, { - "baseAsset": "HEGIC", + "baseAsset": "NEAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -102403,15 +150376,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -102419,7 +150392,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "63847.05486111", + "maxQty": "4890.36348992", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102452,18 +150425,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HEGICETH" + "symbol": "NEARETH" }, { - "baseAsset": "HEGIC", + "baseAsset": "ROSE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -102473,15 +150446,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -102489,7 +150462,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "47998.78265278", + "maxQty": "160606.46629603", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102516,24 +150489,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "HEGICBUSD" + "symbol": "ROSEBNB" }, { - "baseAsset": "AAVEUP", + "baseAsset": "VOXEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "184.21000000", - "minPrice": "9.69600000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -102543,7 +150516,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -102551,7 +150524,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -102559,7 +150532,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2923.72886111", + "maxQty": "12358.29073662", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102574,7 +150547,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -102584,25 +150557,25 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEUPUSDT" + "symbol": "VOXELETH" }, { - "baseAsset": "AAVEDOWN", + "baseAsset": "ALICE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.01583000", - "minPrice": "0.00084000", + "maxPrice": "1000.00000000", + "minPrice": "0.00001000", "tickSize": "0.00001000" }, { @@ -102613,9 +150586,79 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "4155.06468311", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ALICEBNB" + }, + { + "baseAsset": "ATOM", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -102629,7 +150672,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4293116.36213889", + "maxQty": "1210.29440027", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102644,7 +150687,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -102654,18 +150697,88 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "AAVEDOWNUSDT" + "symbol": "ATOMTRY" }, { - "baseAsset": "PROM", + "baseAsset": "ETH", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "999996.00000000", + "minPrice": "0.01000000", + "tickSize": "0.01000000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92233.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "52.18362689", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "UST", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "ETHUST" + }, + { + "baseAsset": "GALA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -102683,15 +150796,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.10000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -102699,7 +150812,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2276.42562500", + "maxQty": "131672.34190410", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102726,24 +150839,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BNB", + "quoteAsset": "AUD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PROMBNB" + "symbol": "GALAAUD" }, { - "baseAsset": "PROM", + "baseAsset": "LRC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -102753,7 +150866,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -102761,7 +150874,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -102769,7 +150882,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2789.31430556", + "maxQty": "39735.59343293", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102796,24 +150909,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PROMBUSD" + "symbol": "LRCBNB" }, { - "baseAsset": "XRP", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -102823,15 +150936,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -102839,7 +150952,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "64025.52029167", + "maxQty": "289223.01445448", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102866,24 +150979,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPBRL" + "symbol": "ONEETH" }, { - "baseAsset": "XRP", + "baseAsset": "OOKI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -102893,15 +151006,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "922320.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "500.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -102909,7 +151022,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50543.89593056", + "maxQty": "1983763.47533009", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -102936,16 +151049,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "NGN", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XRPNGN" + "symbol": "OOKIBNB" }, { - "baseAsset": "SKL", + "baseAsset": "ACH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -102963,7 +151076,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -102979,7 +151092,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1088417.55625000", + "maxQty": "1115112.10632383", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103012,10 +151125,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SKLBTC" + "symbol": "ACHBTC" }, { - "baseAsset": "SKL", + "baseAsset": "ACH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -103033,9 +151146,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -103049,7 +151162,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "498311.76013889", + "maxQty": "1805685.25017373", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103082,10 +151195,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SKLBUSD" + "symbol": "ACHBUSD" }, { - "baseAsset": "SKL", + "baseAsset": "ACH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -103103,9 +151216,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -103119,7 +151232,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1553410.73055556", + "maxQty": "3967680.09659485", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103152,18 +151265,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SKLUSDT" + "symbol": "ACHUSDT" }, { - "baseAsset": "BCH", + "baseAsset": "IMX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -103173,15 +151286,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -103189,7 +151302,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "467.49176210", + "maxQty": "28894.96359277", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103203,7 +151316,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -103214,27 +151327,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BCHEUR" + "symbol": "IMXBTC" }, { - "baseAsset": "YFI", + "baseAsset": "IMX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -103244,9 +151356,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000.00000000", - "minQty": "0.00000100", - "stepSize": "0.00000100" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -103260,7 +151372,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6.09639938", + "maxQty": "38987.26720639", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103274,7 +151386,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -103285,27 +151397,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "YFIEUR" + "symbol": "IMXBUSD" }, { - "baseAsset": "ZIL", + "baseAsset": "IMX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00", - "minPrice": "0.01", - "tickSize": "0.01" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -103315,15 +151426,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "20000.00" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -103331,7 +151442,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "574161.78402778", + "maxQty": "123899.11389159", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103358,16 +151469,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BIDR", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, + "quoteAsset": "USDT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, + "quotePrecision": 8, "status": "TRADING", - "symbol": "ZILBIDR" + "symbol": "IMXUSDT" }, { - "baseAsset": "SUSD", + "baseAsset": "GLMR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -103385,9 +151496,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -103401,7 +151512,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "68272.12291667", + "maxQty": "29163.52578179", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103434,18 +151545,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSDBTC" + "symbol": "GLMRBTC" }, { - "baseAsset": "SUSD", + "baseAsset": "GLMR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -103455,15 +151566,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -103471,7 +151582,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "59028.20069444", + "maxQty": "32012.39937456", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103498,16 +151609,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSDETH" + "symbol": "GLMRBUSD" }, { - "baseAsset": "SUSD", + "baseAsset": "GLMR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -103525,9 +151636,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -103541,7 +151652,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "81686.33388889", + "maxQty": "189798.34697706", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103574,18 +151685,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSDUSDT" + "symbol": "GLMRUSDT" }, { - "baseAsset": "COVER", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000000.00", + "minPrice": "1.00", + "tickSize": "1.00" }, { "avgPriceMins": 5, @@ -103595,15 +151706,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.00010000", - "stepSize": "0.00010000" + "maxQty": "922337194.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "20000.00" }, { "filterType": "ICEBERG_PARTS", @@ -103611,7 +151722,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "16.16605465", + "maxQty": "386.20386379", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103638,24 +151749,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, + "quoteAsset": "BIDR", + "quoteAssetPrecision": 2, + "quoteCommissionPrecision": 2, "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, + "quotePrecision": 2, "status": "TRADING", - "symbol": "COVERETH" + "symbol": "ATOMBIDR" }, { - "baseAsset": "COVER", + "baseAsset": "DYDX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -103665,15 +151776,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -103681,7 +151792,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "58.84346556", + "maxQty": "5225.10024322", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103708,24 +151819,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "COVERBUSD" + "symbol": "DYDXETH" }, { - "baseAsset": "GLM", + "baseAsset": "FARM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -103735,15 +151846,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.00010000", + "stepSize": "0.00010000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -103751,7 +151862,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "345002.72847222", + "maxQty": "156.56793676", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103778,22 +151889,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GLMBTC" + "symbol": "FARMETH" }, { - "baseAsset": "GLM", + "baseAsset": "FOR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00000010", "tickSize": "0.00000010" }, @@ -103805,7 +151916,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -103813,7 +151924,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -103821,7 +151932,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "140388.22291667", + "maxQty": "374801.67963863", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103848,24 +151959,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GLMETH" + "symbol": "FORBNB" }, { - "baseAsset": "GHST", + "baseAsset": "ICP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -103875,15 +151986,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -103891,7 +152002,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "12906.67083333", + "maxQty": "668.61955246", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103918,24 +152029,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GHSTETH" + "symbol": "ICPTRY" }, { - "baseAsset": "GHST", + "baseAsset": "JASMY", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -103945,15 +152056,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -103961,7 +152072,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "119249.09667361", + "maxQty": "1678744.27727588", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -103988,24 +152099,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GHSTBUSD" + "symbol": "JASMYETH" }, { - "baseAsset": "SUSHIUP", + "baseAsset": "LINA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "55.50700000", - "minPrice": "2.92200000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -104015,15 +152126,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -104031,7 +152142,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "8418.48191667", + "maxQty": "853988.87838776", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104046,7 +152157,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -104056,26 +152167,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSHIUPUSDT" + "symbol": "LINABNB" }, { - "baseAsset": "SUSHIDOWN", + "baseAsset": "OOKI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.01719000", - "minPrice": "0.00091000", - "tickSize": "0.00001000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -104085,15 +152196,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -104101,7 +152212,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3478599.64843750", + "maxQty": "3096354.78596247", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104116,7 +152227,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -104126,26 +152237,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "SUSHIDOWNUSDT" + "symbol": "OOKIETH" }, { - "baseAsset": "XLMUP", + "baseAsset": "ROSE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "18.39900000", - "minPrice": "0.96900000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -104155,15 +152266,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -104171,7 +152282,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "21822.18712500", + "maxQty": "131426.41410701", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104186,7 +152297,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -104196,26 +152307,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XLMUPUSDT" + "symbol": "ROSEETH" }, { - "baseAsset": "XLMDOWN", + "baseAsset": "UMA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "0.35820000", - "minPrice": "0.01890000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -104225,9 +152336,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "920000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -104241,7 +152352,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1077030.88563194", + "maxQty": "3621.60354412", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104256,7 +152367,7 @@ ], "icebergAllowed": true, "isMarginTradingAllowed": false, - "isSpotTradingAllowed": false, + "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ "LIMIT", @@ -104266,26 +152377,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "LEVERAGED" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XLMDOWNUSDT" + "symbol": "UMABUSD" }, { - "baseAsset": "LINK", + "baseAsset": "UNI", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -104295,15 +152406,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "45000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -104311,7 +152422,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2030.39546680", + "maxQty": "4580.53563029", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104338,24 +152449,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKBRL" + "symbol": "UNIETH" }, { - "baseAsset": "LINK", + "baseAsset": "XTZ", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000000.00000000", - "minPrice": "1.00000000", - "tickSize": "1.00000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -104365,15 +152476,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "500.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -104381,7 +152492,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "371.66877535", + "maxQty": "7847.90101459", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104408,24 +152519,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "NGN", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKNGN" + "symbol": "XTZETH" }, { - "baseAsset": "LTC", + "baseAsset": "LOKA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00000000", - "minPrice": "0.10000000", - "tickSize": "0.10000000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -104435,15 +152546,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "100.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -104451,7 +152562,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1070.10285029", + "maxQty": "39504.40861709", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104478,24 +152589,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "RUB", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LTCRUB" + "symbol": "LOKABTC" }, { - "baseAsset": "TRX", + "baseAsset": "LOKA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -104505,15 +152616,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -104521,7 +152632,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1515716.26527778", + "maxQty": "50208.36712995", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104548,24 +152659,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TRY", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRXTRY" + "symbol": "LOKABNB" }, { - "baseAsset": "XLM", + "baseAsset": "LOKA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00001000", - "tickSize": "0.00001000" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -104575,7 +152686,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.10000000", "stepSize": "0.10000000" }, @@ -104591,7 +152702,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "554105.62520833", + "maxQty": "94895.19541348", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104618,24 +152729,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XLMEUR" + "symbol": "LOKABUSD" }, { - "baseAsset": "DF", + "baseAsset": "LOKA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "1000.00000000", + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -104645,15 +152756,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -104661,7 +152772,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "50648.52013889", + "maxQty": "164110.44378040", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104688,24 +152799,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DFETH" + "symbol": "LOKAUSDT" }, { - "baseAsset": "DF", + "baseAsset": "ATOM", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -104715,9 +152826,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -104731,7 +152842,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "32935.84045139", + "maxQty": "365.51780611", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104758,24 +152869,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BRL", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DFBUSD" + "symbol": "ATOMBRL" }, { - "baseAsset": "GRT", + "baseAsset": "BNB", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -104785,15 +152896,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -104801,7 +152912,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "682077.18750000", + "maxQty": "87.26523558", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104815,7 +152926,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -104826,27 +152937,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "UST", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GRTBTC" + "symbol": "BNBUST" }, { - "baseAsset": "GRT", + "baseAsset": "CRV", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -104856,15 +152966,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -104872,7 +152982,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "498423.39236111", + "maxQty": "9936.61596942", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104886,7 +152996,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -104897,8 +153007,7 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], "quoteAsset": "ETH", "quoteAssetPrecision": 8, @@ -104906,10 +153015,10 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GRTETH" + "symbol": "CRVETH" }, { - "baseAsset": "GRT", + "baseAsset": "HIGH", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -104927,15 +153036,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -104943,7 +153052,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2090982.90493056", + "maxQty": "5692.17219596", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -104957,7 +153066,7 @@ } ], "icebergAllowed": true, - "isMarginTradingAllowed": true, + "isMarginTradingAllowed": false, "isSpotTradingAllowed": true, "ocoAllowed": true, "orderTypes": [ @@ -104968,27 +153077,26 @@ "TAKE_PROFIT_LIMIT" ], "permissions": [ - "SPOT", - "MARGIN" + "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GRTUSDT" + "symbol": "HIGHBNB" }, { - "baseAsset": "JUV", + "baseAsset": "NEAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "maxPrice": "92233.00000000", + "minPrice": "1.00000000", + "tickSize": "1.00000000" }, { "avgPriceMins": 5, @@ -104998,15 +153106,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92233.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "100.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -105014,7 +153122,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1478.56009028", + "maxQty": "1098.77636205", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105041,22 +153149,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "RUB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "JUVBTC" + "symbol": "NEARRUB" }, { - "baseAsset": "JUV", + "baseAsset": "ROSE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -105068,9 +153176,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -105084,7 +153192,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "302.28756458", + "maxQty": "81750.82390548", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105111,22 +153219,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "JUVBUSD" + "symbol": "ROSETRY" }, { - "baseAsset": "JUV", + "baseAsset": "SCRT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -105138,9 +153246,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -105154,7 +153262,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1506.96131736", + "maxQty": "31614.09708130", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105187,18 +153295,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "JUVUSDT" + "symbol": "SCRTUSDT" }, { - "baseAsset": "PSG", + "baseAsset": "API3", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -105208,7 +153316,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -105224,7 +153332,77 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "877.67152778", + "maxQty": "11694.35706045", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BTC", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "API3BTC" + }, + { + "baseAsset": "API3", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00100000", + "tickSize": "0.00100000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "10.00000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16204.05402362", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105251,22 +153429,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PSGBTC" + "symbol": "API3BUSD" }, { - "baseAsset": "PSG", + "baseAsset": "API3", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -105278,9 +153456,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -105294,7 +153472,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "891.43169097", + "maxQty": "77726.70296038", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105321,24 +153499,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PSGBUSD" + "symbol": "API3USDT" }, { - "baseAsset": "PSG", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, + "baseAsset": "BTTC", + "baseAssetPrecision": 1, + "baseCommissionPrecision": 1, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "0.10000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -105348,9 +153526,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92233720368.0", + "minQty": "1.0", + "stepSize": "1.0" }, { "applyToMarket": true, @@ -105364,9 +153542,9 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3184.58923681", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "maxQty": "92233720368.0", + "minQty": "0.0", + "stepSize": "0.0" }, { "filterType": "MAX_NUM_ORDERS", @@ -105397,156 +153575,16 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PSGUSDT" - }, - { - "baseAsset": "BUSD", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "30000.00" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "24928.85730556", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BVND", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, - "status": "TRADING", - "symbol": "BUSDBVND" + "symbol": "BTTCUSDT" }, { - "baseAsset": "USDT", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, + "baseAsset": "BTTC", + "baseAssetPrecision": 1, + "baseCommissionPrecision": 1, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000000.00", - "minPrice": "1.00", - "tickSize": "1.00" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "1000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "30000.00" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 - }, - { - "filterType": "MARKET_LOT_SIZE", - "maxQty": "39146.04860417", - "minQty": "0.00000000", - "stepSize": "0.00000000" - }, - { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BVND", - "quoteAssetPrecision": 2, - "quoteCommissionPrecision": 2, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 2, - "status": "TRADING", - "symbol": "USDTBVND" - }, - { - "baseAsset": "1INCH", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", + "maxPrice": "0.10000000", "minPrice": "0.00000001", "tickSize": "0.00000001" }, @@ -105558,15 +153596,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92233720368.0", + "minQty": "1.0", + "stepSize": "1.0" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -105574,9 +153612,9 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "79221.49770833", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "maxQty": "4006526823.3", + "minQty": "0.0", + "stepSize": "0.0" }, { "filterType": "MAX_NUM_ORDERS", @@ -105601,24 +153639,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "1INCHBTC" + "symbol": "BTTCUSDC" }, { - "baseAsset": "1INCH", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, + "baseAsset": "BTTC", + "baseAssetPrecision": 1, + "baseCommissionPrecision": 1, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "0.10000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -105628,9 +153666,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92233720368.0", + "minQty": "1.0", + "stepSize": "1.0" }, { "applyToMarket": true, @@ -105644,9 +153682,9 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "198927.59741667", - "minQty": "0.00000000", - "stepSize": "0.00000000" + "maxQty": "22083971568.3", + "minQty": "0.0", + "stepSize": "0.0" }, { "filterType": "MAX_NUM_ORDERS", @@ -105671,16 +153709,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "1INCHUSDT" + "symbol": "BTTCTRY" }, { - "baseAsset": "REEF", + "baseAsset": "ACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -105698,9 +153736,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -105714,7 +153752,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "29015066.20486111", + "maxQty": "25782.75566365", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105747,18 +153785,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "REEFBTC" + "symbol": "ACABTC" }, { - "baseAsset": "REEF", + "baseAsset": "ACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -105768,9 +153806,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -105784,7 +153822,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "15790566.15000000", + "maxQty": "19383.73407922", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105811,24 +153849,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "REEFUSDT" + "symbol": "ACABUSD" }, { - "baseAsset": "OG", + "baseAsset": "ACA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -105838,7 +153876,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -105846,7 +153884,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -105854,7 +153892,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "3112.51177778", + "maxQty": "136809.11118832", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105881,24 +153919,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OGBTC" + "symbol": "ACAUSDT" }, { - "baseAsset": "OG", + "baseAsset": "ANC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -105908,15 +153946,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -105924,7 +153962,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6038.43955208", + "maxQty": "11177.67972897", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -105951,24 +153989,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "OGUSDT" + "symbol": "ANCBTC" }, { - "baseAsset": "ATM", + "baseAsset": "ANC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -105978,7 +154016,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -105986,7 +154024,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -105994,7 +154032,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1551.37294444", + "maxQty": "21552.53316191", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106021,22 +154059,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ATMBTC" + "symbol": "ANCBUSD" }, { - "baseAsset": "ATM", + "baseAsset": "ANC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -106048,9 +154086,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -106064,7 +154102,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4389.15056944", + "maxQty": "84023.76920083", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106097,18 +154135,88 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ATMUSDT" + "symbol": "ANCUSDT" }, { - "baseAsset": "ASR", + "baseAsset": "BDOT", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.5", + "multiplierUp": "2" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.30000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "87388.77391938", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "DOT", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "BDOTDOT" + }, + { + "baseAsset": "XNO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -106118,7 +154226,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -106134,7 +154242,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2597.26297222", + "maxQty": "13935.80735232", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106167,18 +154275,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ASRBTC" + "symbol": "XNOBTC" }, { - "baseAsset": "ASR", + "baseAsset": "XNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -106188,15 +154296,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -106204,7 +154312,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4548.12744583", + "maxQty": "23928.98744961", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106231,24 +154339,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "ASRUSDT" + "symbol": "XNOETH" }, { - "baseAsset": "CELO", + "baseAsset": "XNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -106258,15 +154366,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -106274,7 +154382,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "23897.70402778", + "maxQty": "14806.64702571", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106301,24 +154409,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CELOBTC" + "symbol": "XNOBUSD" }, { - "baseAsset": "CELO", + "baseAsset": "XNO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00100000", + "tickSize": "0.00100000" }, { "avgPriceMins": 5, @@ -106328,7 +154436,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -106344,7 +154452,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "55944.15875000", + "maxQty": "29523.37714384", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106377,18 +154485,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CELOUSDT" + "symbol": "XNOUSDT" }, { - "baseAsset": "RIF", + "baseAsset": "COS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -106398,7 +154506,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", + "maxQty": "92141578.00000000", "minQty": "1.00000000", "stepSize": "1.00000000" }, @@ -106406,7 +154514,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -106414,7 +154522,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "301518.58819444", + "maxQty": "975050.68380820", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106441,24 +154549,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RIFBTC" + "symbol": "COSTRY" }, { - "baseAsset": "RIF", + "baseAsset": "KAVA", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -106468,7 +154576,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -106476,7 +154584,7 @@ "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -106484,7 +154592,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "157599.78041667", + "maxQty": "4882.72777623", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106511,24 +154619,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "RIFUSDT" + "symbol": "KAVAETH" }, { - "baseAsset": "CHZ", + "baseAsset": "MC", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" }, { "avgPriceMins": 5, @@ -106538,15 +154646,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "10000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -106554,7 +154662,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2082390.72777778", + "maxQty": "13335.23879082", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106581,22 +154689,22 @@ "permissions": [ "SPOT" ], - "quoteAsset": "TRY", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CHZTRY" + "symbol": "MCBNB" }, { - "baseAsset": "XLM", + "baseAsset": "ONE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "100000.00000000", + "maxPrice": "1000.00000000", "minPrice": "0.00100000", "tickSize": "0.00100000" }, @@ -106608,9 +154716,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "100000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -106624,7 +154732,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "226156.86302778", + "maxQty": "98754.32460041", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106657,18 +154765,18 @@ "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "XLMTRY" + "symbol": "ONETRY" }, { - "baseAsset": "LINK", + "baseAsset": "WOO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -106678,15 +154786,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", @@ -106694,7 +154802,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "6090.75470625", + "maxQty": "35074.44940931", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106721,16 +154829,86 @@ "permissions": [ "SPOT" ], - "quoteAsset": "GBP", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LINKGBP" + "symbol": "WOOBTC" }, { - "baseAsset": "GRT", + "baseAsset": "WOO", + "baseAssetPrecision": 8, + "baseCommissionPrecision": 8, + "filters": [ + { + "filterType": "PRICE_FILTER", + "maxPrice": "1000.00000000", + "minPrice": "0.00000100", + "tickSize": "0.00000100" + }, + { + "avgPriceMins": 5, + "filterType": "PERCENT_PRICE", + "multiplierDown": "0.2", + "multiplierUp": "5" + }, + { + "filterType": "LOT_SIZE", + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" + }, + { + "applyToMarket": true, + "avgPriceMins": 5, + "filterType": "MIN_NOTIONAL", + "minNotional": "0.05000000" + }, + { + "filterType": "ICEBERG_PARTS", + "limit": 10 + }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "102174.61125781", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, + { + "filterType": "MAX_NUM_ORDERS", + "maxNumOrders": 200 + }, + { + "filterType": "MAX_NUM_ALGO_ORDERS", + "maxNumAlgoOrders": 5 + } + ], + "icebergAllowed": true, + "isMarginTradingAllowed": false, + "isSpotTradingAllowed": true, + "ocoAllowed": true, + "orderTypes": [ + "LIMIT", + "LIMIT_MAKER", + "MARKET", + "STOP_LOSS_LIMIT", + "TAKE_PROFIT_LIMIT" + ], + "permissions": [ + "SPOT" + ], + "quoteAsset": "BNB", + "quoteAssetPrecision": 8, + "quoteCommissionPrecision": 8, + "quoteOrderQtyMarketAllowed": true, + "quotePrecision": 8, + "status": "TRADING", + "symbol": "WOOBNB" + }, + { + "baseAsset": "WOO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -106748,9 +154926,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, @@ -106764,7 +154942,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "230610.07822222", + "maxQty": "58679.29367616", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106791,24 +154969,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "GRTEUR" + "symbol": "WOOBUSD" }, { - "baseAsset": "BTCST", + "baseAsset": "WOO", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000010", - "tickSize": "0.00000010" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -106818,15 +154996,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -106834,7 +155012,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "631.37843750", + "maxQty": "263332.63842946", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106861,24 +155039,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCSTBTC" + "symbol": "WOOUSDT" }, { - "baseAsset": "BTCST", + "baseAsset": "CELR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -106888,15 +155066,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00500000" }, { "filterType": "ICEBERG_PARTS", @@ -106904,7 +155082,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "325.45458333", + "maxQty": "447384.22168172", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -106931,24 +155109,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "ETH", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCSTBUSD" + "symbol": "CELRETH" }, { - "baseAsset": "BTCST", + "baseAsset": "PEOPLE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "1000.00000000", + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -106958,15 +155136,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -106974,7 +155152,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "1128.65803056", + "maxQty": "430577.36761640", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107001,16 +155179,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "BTCSTUSDT" + "symbol": "PEOPLEBNB" }, { - "baseAsset": "TRU", + "baseAsset": "SLP", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -107028,15 +155206,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.10000000", - "stepSize": "0.10000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -107044,7 +155222,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "494359.84951389", + "maxQty": "2959532.69770674", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107071,24 +155249,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BTC", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRUBTC" + "symbol": "SLPBNB" }, { - "baseAsset": "TRU", + "baseAsset": "SPELL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -107098,15 +155276,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -107114,7 +155292,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "17663.71210417", + "maxQty": "4685571.49895760", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107141,24 +155319,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRUBUSD" + "symbol": "SPELLBNB" }, { - "baseAsset": "TRU", + "baseAsset": "SPELL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00001000", + "tickSize": "0.00001000" }, { "avgPriceMins": 5, @@ -107168,9 +155346,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, @@ -107184,7 +155362,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "483550.05779861", + "maxQty": "5820533.37317581", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107211,24 +155389,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TRUUSDT" + "symbol": "SPELLTRY" }, { - "baseAsset": "DEXE", + "baseAsset": "TFUEL", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -107238,15 +155416,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "1.00000000", + "stepSize": "1.00000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.01000000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", @@ -107254,7 +155432,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "4949.03008333", + "maxQty": "80854.40653231", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107281,24 +155459,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "ETH", + "quoteAsset": "BUSD", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DEXEETH" + "symbol": "TFUELBUSD" }, { - "baseAsset": "DEXE", + "baseAsset": "AXS", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", - "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "maxPrice": "100000.00000000", + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -107308,7 +155486,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", + "maxQty": "922327.00000000", "minQty": "0.00100000", "stepSize": "0.00100000" }, @@ -107324,7 +155502,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "2247.63268194", + "maxQty": "223.73892842", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107351,24 +155529,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "DEXEBUSD" + "symbol": "AXSTRY" }, { - "baseAsset": "EOS", + "baseAsset": "DAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "10000.00000000", - "minPrice": "0.00100000", - "tickSize": "0.00100000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -107378,9 +155556,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000.00000000", - "minQty": "0.00100000", - "stepSize": "0.00100000" + "maxQty": "9222449.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, @@ -107394,7 +155572,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "21448.27897986", + "maxQty": "14122.02200833", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107421,24 +155599,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "EUR", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "EOSEUR" + "symbol": "DARTRY" }, { - "baseAsset": "LTC", + "baseAsset": "NEAR", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "100000.00000000", - "minPrice": "0.01000000", - "tickSize": "0.01000000" + "minPrice": "0.10000000", + "tickSize": "0.10000000" }, { "avgPriceMins": 5, @@ -107448,9 +155626,9 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "45000.00000000", - "minQty": "0.00001000", - "stepSize": "0.00001000" + "maxQty": "922327.00000000", + "minQty": "0.00100000", + "stepSize": "0.00100000" }, { "applyToMarket": true, @@ -107464,7 +155642,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "204.97894137", + "maxQty": "1454.12340931", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107491,24 +155669,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BRL", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "LTCBRL" + "symbol": "NEARTRY" }, { - "baseAsset": "USDC", + "baseAsset": "IDEX", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.00000010", + "tickSize": "0.00000010" }, { "avgPriceMins": 5, @@ -107518,15 +155696,15 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", - "minQty": "0.01000000", - "stepSize": "0.01000000" + "maxQty": "92141578.00000000", + "minQty": "0.10000000", + "stepSize": "0.10000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.05000000" }, { "filterType": "ICEBERG_PARTS", @@ -107534,7 +155712,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "275717.73602083", + "maxQty": "144093.82056984", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107561,16 +155739,16 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "BNB", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "USDCBUSD" + "symbol": "IDEXBNB" }, { - "baseAsset": "TUSD", + "baseAsset": "ALPINE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ @@ -107588,7 +155766,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -107604,7 +155782,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "282364.24250694", + "maxQty": "2976.48259902", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107631,24 +155809,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "EUR", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "TUSDBUSD" + "symbol": "ALPINEEUR" }, { - "baseAsset": "PAX", + "baseAsset": "ALPINE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00010000", - "tickSize": "0.00010000" + "minPrice": "0.01000000", + "tickSize": "0.01000000" }, { "avgPriceMins": 5, @@ -107658,7 +155836,7 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "900000.00000000", + "maxQty": "92141578.00000000", "minQty": "0.01000000", "stepSize": "0.01000000" }, @@ -107674,7 +155852,7 @@ }, { "filterType": "MARKET_LOT_SIZE", - "maxQty": "226366.83949306", + "maxQty": "21411.15371091", "minQty": "0.00000000", "stepSize": "0.00000000" }, @@ -107701,24 +155879,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "TRY", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "PAXBUSD" + "symbol": "ALPINETRY" }, { - "baseAsset": "CKB", + "baseAsset": "ALPINE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000001", - "tickSize": "0.00000001" + "minPrice": "0.00010000", + "tickSize": "0.00010000" }, { "avgPriceMins": 5, @@ -107728,83 +155906,25 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "90000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "0.00010000" + "minNotional": "10.00000000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, { - "filterType": "MAX_NUM_ORDERS", - "maxNumOrders": 200 - }, - { - "filterType": "MAX_NUM_ALGO_ORDERS", - "maxNumAlgoOrders": 5 - } - ], - "icebergAllowed": true, - "isMarginTradingAllowed": false, - "isSpotTradingAllowed": true, - "ocoAllowed": true, - "orderTypes": [ - "LIMIT", - "LIMIT_MAKER", - "MARKET", - "STOP_LOSS_LIMIT", - "TAKE_PROFIT_LIMIT" - ], - "permissions": [ - "SPOT" - ], - "quoteAsset": "BTC", - "quoteAssetPrecision": 8, - "quoteCommissionPrecision": 8, - "quoteOrderQtyMarketAllowed": true, - "quotePrecision": 8, - "status": "TRADING", - "symbol": "CKBBTC" - }, - { - "baseAsset": "CKB", - "baseAssetPrecision": 8, - "baseCommissionPrecision": 8, - "filters": [ - { - "filterType": "PRICE_FILTER", - "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" - }, - { - "avgPriceMins": 5, - "filterType": "PERCENT_PRICE", - "multiplierDown": "0.2", - "multiplierUp": "5" - }, - { - "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" - }, - { - "applyToMarket": true, - "avgPriceMins": 5, - "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" - }, - { - "filterType": "ICEBERG_PARTS", - "limit": 10 + "filterType": "MARKET_LOT_SIZE", + "maxQty": "68041.27897845", + "minQty": "0.00000000", + "stepSize": "0.00000000" }, { "filterType": "MAX_NUM_ORDERS", @@ -107829,24 +155949,24 @@ "permissions": [ "SPOT" ], - "quoteAsset": "BUSD", + "quoteAsset": "USDT", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CKBBUSD" + "symbol": "ALPINEUSDT" }, { - "baseAsset": "CKB", + "baseAsset": "ALPINE", "baseAssetPrecision": 8, "baseCommissionPrecision": 8, "filters": [ { "filterType": "PRICE_FILTER", "maxPrice": "1000.00000000", - "minPrice": "0.00000100", - "tickSize": "0.00000100" + "minPrice": "0.00000001", + "tickSize": "0.00000001" }, { "avgPriceMins": 5, @@ -107856,20 +155976,26 @@ }, { "filterType": "LOT_SIZE", - "maxQty": "9000000.00000000", - "minQty": "1.00000000", - "stepSize": "1.00000000" + "maxQty": "92141578.00000000", + "minQty": "0.01000000", + "stepSize": "0.01000000" }, { "applyToMarket": true, "avgPriceMins": 5, "filterType": "MIN_NOTIONAL", - "minNotional": "10.00000000" + "minNotional": "0.00010000" }, { "filterType": "ICEBERG_PARTS", "limit": 10 }, + { + "filterType": "MARKET_LOT_SIZE", + "maxQty": "16103.29022932", + "minQty": "0.00000000", + "stepSize": "0.00000000" + }, { "filterType": "MAX_NUM_ORDERS", "maxNumOrders": 200 @@ -107893,13 +156019,13 @@ "permissions": [ "SPOT" ], - "quoteAsset": "USDT", + "quoteAsset": "BTC", "quoteAssetPrecision": 8, "quoteCommissionPrecision": 8, "quoteOrderQtyMarketAllowed": true, "quotePrecision": 8, "status": "TRADING", - "symbol": "CKBUSDT" + "symbol": "ALPINEBTC" } ], "timezone": "UTC" diff --git a/web/package-lock.json b/web/package-lock.json index 8a61256e5d3..c4bb64ae04a 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -5094,9 +5094,9 @@ } }, "follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" }, "for-in": { "version": "1.0.2", From 831608ef259bab781935ff7afb553d0e7eb1577a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 2 Mar 2022 16:37:46 +1100 Subject: [PATCH 085/171] Investigating issues with pnl and holdings --- backtester/engine/backtest.go | 4 ++-- .../eventhandlers/portfolio/portfolio.go | 20 +++++++++++-------- backtester/funding/collateral.go | 3 +++ backtester/funding/funding.go | 17 ++++++++-------- backtester/funding/funding_types.go | 2 +- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 2b38071f800..a5c7b88720f 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -99,7 +99,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { if ev.GetAssetType().IsFutures() { // hardcoded fix - err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) + err = bt.Funding.UpdateCollateral(ev) if err != nil { return err } @@ -343,7 +343,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } - err = bt.Funding.UpdateCollateral(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) + err = bt.Funding.UpdateCollateral(ev) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 11aab8c4eda..59d6bbcdc23 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -39,7 +39,9 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi if funds == nil { return nil, funding.ErrFundsNotFound } - + if ev.GetDirection() == common.ClosePosition { + log.Debugln(log.Currency, "") + } o := &order.Order{ Base: event.Base{ Offset: ev.GetOffset(), @@ -215,10 +217,12 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi case gctorder.Sell: err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) sizedOrder.AllocatedFunds = sizedOrder.Amount - case gctorder.Short, gctorder.Long, common.ClosePosition: - - err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) - sizedOrder.AllocatedFunds = sizedOrder.Amount.Div(sizedOrder.Price) + case gctorder.Short, gctorder.Long: + err = funds.Reserve(sizedOrder.Amount.Mul(originalOrderSignal.Price), d.GetDirection()) + sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) + case common.ClosePosition: + err = funds.Reserve(sizedOrder.Amount.Mul(originalOrderSignal.Price), d.GetDirection()) + sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) default: err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.Price), gctorder.Buy) sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) @@ -231,7 +235,7 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi } // OnFill processes the event after an order has been placed by the exchange. Its purpose is to track holdings for future portfolio decisions. -func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (fill.Event, error) { +func (p *Portfolio) OnFill(ev fill.Event, funds funding.IFundReleaser) (fill.Event, error) { if ev == nil { return nil, common.ErrNilEvent } @@ -242,7 +246,7 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (fill.E var err error if ev.GetAssetType() == asset.Spot { - fp, err := funding.GetPairReleaser() + fp, err := funds.GetPairReleaser() if err != nil { return nil, err } @@ -253,7 +257,7 @@ func (p *Portfolio) OnFill(ev fill.Event, funding funding.IFundReleaser) (fill.E } else { h = lookup.GetLatestHoldings() if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, funding) + h, err = holdings.Create(ev, funds) if err != nil { return nil, err } diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 310419f4c4f..682d669a813 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -112,6 +113,8 @@ func (c *Collateral) Reserve(amount decimal.Decimal, side order.Side) error { switch side { case order.Long, order.Short: return c.Collateral.Reserve(amount) + case common.ClosePosition: + return c.Collateral.Release(amount, amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", errCannotAllocate, diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 97ed4ba80eb..b8402b8731d 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -478,14 +478,11 @@ func (f *FundManager) GetAllFunding() []BasicItem { return result } -func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, pair currency.Pair) error { +func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { exchMap := make(map[string]exchange.IBotExchange) var collateralAmount decimal.Decimal var err error for i := range f.items { - if f.items[i].asset != asset.Spot { - continue - } exch, ok := exchMap[f.items[i].exchange] if !ok { exch, err = f.exchangeManager.GetExchangeByName(f.items[i].exchange) @@ -501,6 +498,7 @@ func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, pair cu usd = latest.GetClosePrice() } } + // ????? var side = gctorder.Buy if !f.items[i].available.GreaterThan(decimal.Zero) { side = gctorder.Sell @@ -514,23 +512,26 @@ func (f *FundManager) UpdateCollateral(exchName string, item asset.Item, pair cu USDPrice: usd, }) if err != nil { + if errors.Is(err, gctorder.ErrUSDValueRequired) { + continue + } return err } collateralAmount = latest.AvailableForUseAsCollateral } - collat, err := exchMap[exchName].GetCollateralCurrencyForContract(item, pair) + collat, err := exchMap[ev.GetExchange()].GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) if err != nil { return err } for i := range f.items { - if f.items[i].exchange == exchName && - f.items[i].asset == item && + if f.items[i].exchange == ev.GetExchange() && + f.items[i].asset == ev.GetAssetType() && f.items[i].currency.Equal(collat) { f.items[i].available = collateralAmount return nil } } - return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, exchName, item, collat) + return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, ev.GetExchange(), ev.GetAssetType(), collat) } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index e1d81349424..576405f5511 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -35,7 +35,7 @@ type IFundingManager interface { USDTrackingDisabled() bool LiquidateByCollateral(currency.Code) error GetAllFunding() []BasicItem - UpdateCollateral(string, asset.Item, currency.Pair) error + UpdateCollateral(common.EventHandler) error } // IFundingReader is a simple interface of From 45d85fc19071f9165f0b0a5cbc0683c79d726c62 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 4 Mar 2022 10:00:09 +1100 Subject: [PATCH 086/171] Minor pnl fixes --- backtester/engine/setup.go | 5 +++-- backtester/funding/funding.go | 34 +++++++++++++++++++------------- exchanges/exchange.go | 4 ++-- exchanges/ftx/ftx_wrapper.go | 7 +++++-- exchanges/order/futures_types.go | 2 +- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index 0e6178cc067..a467ac314cb 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -285,7 +285,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool } var collateralCurrency currency.Code - collateralCurrency, err = exch.GetCollateralCurrencyForContract(a, currency.NewPair(b, q)) + collateralCurrency, _, err = exch.GetCollateralCurrencyForContract(a, currency.NewPair(b, q)) if err != nil { return nil, err } @@ -767,7 +767,8 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, // eg BTC-PERP on FTX has a collateral currency of USD // taking the BTC base and USD as quote, allows linking // BTC-USD and BTC-PERP - curr, err := exch.GetCollateralCurrencyForContract(a, fPair) + var curr currency.Code + curr, _, err = exch.GetCollateralCurrencyForContract(a, fPair) if err != nil { return resp, err } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index b8402b8731d..793f318eb8b 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -482,6 +482,10 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { exchMap := make(map[string]exchange.IBotExchange) var collateralAmount decimal.Decimal var err error + butts := gctorder.TotalCollateralCalculator{ + CalculateOffline: true, + } + for i := range f.items { exch, ok := exchMap[f.items[i].exchange] if !ok { @@ -498,40 +502,42 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { usd = latest.GetClosePrice() } } - // ????? var side = gctorder.Buy if !f.items[i].available.GreaterThan(decimal.Zero) { side = gctorder.Sell } - latest, err := exch.ScaleCollateral(context.TODO(), "", &gctorder.CollateralCalculator{ + if usd.IsZero() { + continue + } + butts.CollateralAssets = append(butts.CollateralAssets, gctorder.CollateralCalculator{ CalculateOffline: true, CollateralCurrency: f.items[i].currency, Asset: f.items[i].asset, Side: side, FreeCollateral: f.items[i].available, + LockedCollateral: f.items[i].reserved, USDPrice: usd, }) - if err != nil { - if errors.Is(err, gctorder.ErrUSDValueRequired) { - continue - } - return err - } - collateralAmount = latest.AvailableForUseAsCollateral } - collat, err := exchMap[ev.GetExchange()].GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) + futureCurrency, futureAsset, err := exchMap[ev.GetExchange()].GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) + if err != nil { + return err + } + + collat, err := exchMap[ev.GetExchange()].CalculateTotalCollateral(context.TODO(), &butts) if err != nil { return err } for i := range f.items { if f.items[i].exchange == ev.GetExchange() && - f.items[i].asset == ev.GetAssetType() && - f.items[i].currency.Equal(collat) { - f.items[i].available = collateralAmount + f.items[i].asset == futureAsset && + f.items[i].currency.Equal(futureCurrency) { + f.items[i].available = collat.AvailableCollateral + f.items[i].reserved = collat.UsedCollateral return nil } } - return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, ev.GetExchange(), ev.GetAssetType(), collat) + return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, ev.GetExchange(), ev.GetAssetType(), futureCurrency) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index faa54ff2307..0fe348dda66 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1439,6 +1439,6 @@ func (b *Base) GetFuturesPositions(context.Context, asset.Item, currency.Pair, t } // GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair -func (b *Base) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) { - return currency.Code{}, common.ErrNotYetImplemented +func (b *Base) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, asset.Item, error) { + return currency.Code{}, "", common.ErrNotYetImplemented } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 7ee94b03b19..8f2df05907c 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1672,6 +1672,9 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency } // GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair -func (f *FTX) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) { - return currency.USD, nil +func (f *FTX) GetCollateralCurrencyForContract(i asset.Item, cp currency.Pair) (currency.Code, asset.Item, error) { + if !i.IsFutures() { + return currency.Code{}, "", fmt.Errorf("%v %w", i, order.ErrNotFuturesAsset) + } + return currency.USD, asset.Futures, nil } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 3219dce7a4c..f9478a7ade8 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -58,7 +58,7 @@ type PNLCalculation interface { // multiple ways of calculating the size of collateral // on an exchange type CollateralManagement interface { - GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, error) + GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, asset.Item, error) ScaleCollateral(ctx context.Context, subAccount string, calculator *CollateralCalculator) (*CollateralByCurrency, error) CalculateTotalCollateral(context.Context, *TotalCollateralCalculator) (*TotalCollateralResponse, error) } From d1be63111dc1cfd29bc480d60e8f7afef3696636 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 4 Mar 2022 16:20:27 +1100 Subject: [PATCH 087/171] Fixes future position sizing, needs contract sizing --- .../config/examples/ftx-cash-carry.strat | 2 +- backtester/eventhandlers/exchange/exchange.go | 4 +- .../eventhandlers/exchange/exchange_test.go | 48 +++++++++---------- .../eventhandlers/portfolio/portfolio.go | 19 +++++--- .../eventhandlers/portfolio/size/size.go | 2 +- backtester/eventtypes/order/order.go | 2 +- backtester/eventtypes/order/order_test.go | 2 +- backtester/eventtypes/order/order_types.go | 2 +- backtester/funding/funding.go | 8 ++-- 9 files changed, 47 insertions(+), 42 deletions(-) diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 5b47e4a9eff..04e582baf85 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -10,7 +10,7 @@ "exchange-name": "ftx", "asset": "spot", "currency": "USD", - "initial-funds": "100000", + "initial-funds": "100", "transfer-fee": "0" } ], diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 3adbb0d9099..ec0ece7b5b8 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -76,7 +76,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.Slippage = adjustedPrice.Sub(f.ClosePrice).Div(f.ClosePrice).Mul(decimal.NewFromInt(100)) } else { slippageRate := slippage.EstimateSlippagePercentage(cs.MinimumSlippageRate, cs.MaximumSlippageRate) - if cs.SkipCandleVolumeFitting || o.IsClosingPosition() { + if cs.SkipCandleVolumeFitting || o.GetAssetType().IsFutures() { f.VolumeAdjustedPrice = f.ClosePrice amount = f.Amount } else { @@ -193,7 +193,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ords[i].CloseTime = o.GetTime() f.Order = &ords[i] f.PurchasePrice = decimal.NewFromFloat(ords[i].Price) - if ords[i].AssetType.IsFutures() { + if ords[i].AssetType.IsFutures() || f.GetDirection() == common.ClosePosition { f.Total = limitReducedAmount.Add(f.ExchangeFee) } else { f.Total = f.PurchasePrice.Mul(limitReducedAmount).Add(f.ExchangeFee) diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index c4b5b356068..050ad478587 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -260,10 +260,10 @@ func TestExecuteOrder(t *testing.T) { AssetType: a, } o := &order.Order{ - Base: ev, - Direction: gctorder.Buy, - Amount: decimal.NewFromInt(10), - AllocatedFunds: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Buy, + Amount: decimal.NewFromInt(10), + AllocatedSize: decimal.NewFromInt(1337), } d := &kline.DataFromKline{ @@ -381,10 +381,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { AssetType: a, } o := &order.Order{ - Base: ev, - Direction: gctorder.Buy, - Amount: decimal.NewFromInt(10), - AllocatedFunds: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Buy, + Amount: decimal.NewFromInt(10), + AllocatedSize: decimal.NewFromInt(1337), } d := &kline.DataFromKline{ @@ -413,10 +413,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { t.Errorf("received %v expected %v", err, errExceededPortfolioLimit) } o = &order.Order{ - Base: ev, - Direction: gctorder.Buy, - Amount: decimal.NewFromInt(10), - AllocatedFunds: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Buy, + Amount: decimal.NewFromInt(10), + AllocatedSize: decimal.NewFromInt(1337), } cs.BuySide.MaximumSize = decimal.Zero cs.BuySide.MinimumSize = decimal.NewFromFloat(0.01) @@ -429,10 +429,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { t.Error("limitReducedAmount adjusted to 0.99999999, direction BUY, should fall in buyside {MinimumSize:0.01 MaximumSize:0 MaximumTotal:0}") } o = &order.Order{ - Base: ev, - Direction: gctorder.Sell, - Amount: decimal.NewFromInt(10), - AllocatedFunds: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Sell, + Amount: decimal.NewFromInt(10), + AllocatedSize: decimal.NewFromInt(1337), } cs.SellSide.MaximumSize = decimal.Zero cs.SellSide.MinimumSize = decimal.NewFromFloat(0.01) @@ -446,10 +446,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { } o = &order.Order{ - Base: ev, - Direction: gctorder.Sell, - Amount: decimal.NewFromFloat(0.5), - AllocatedFunds: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Sell, + Amount: decimal.NewFromFloat(0.5), + AllocatedSize: decimal.NewFromInt(1337), } cs.SellSide.MaximumSize = decimal.Zero cs.SellSide.MinimumSize = decimal.NewFromInt(1) @@ -460,10 +460,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { } o = &order.Order{ - Base: ev, - Direction: gctorder.Sell, - Amount: decimal.NewFromFloat(0.02), - AllocatedFunds: decimal.NewFromFloat(0.01337), + Base: ev, + Direction: gctorder.Sell, + Amount: decimal.NewFromFloat(0.02), + AllocatedSize: decimal.NewFromFloat(0.01337), } cs.SellSide.MaximumSize = decimal.Zero cs.SellSide.MinimumSize = decimal.NewFromFloat(0.01) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 59d6bbcdc23..bc139faf9de 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -216,16 +216,16 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi switch d.GetDirection() { case gctorder.Sell: err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) - sizedOrder.AllocatedFunds = sizedOrder.Amount + sizedOrder.AllocatedSize = sizedOrder.Amount case gctorder.Short, gctorder.Long: - err = funds.Reserve(sizedOrder.Amount.Mul(originalOrderSignal.Price), d.GetDirection()) - sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) + err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.Price) case common.ClosePosition: - err = funds.Reserve(sizedOrder.Amount.Mul(originalOrderSignal.Price), d.GetDirection()) - sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) + err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.Price) default: err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.Price), gctorder.Buy) - sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.Price) + sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.Price) } if err != nil { sizedOrder.Direction = common.DoNothing @@ -537,13 +537,18 @@ func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) if len(pos) == 0 { return nil, fmt.Errorf("%w should not happen", errNoHoldings) } + amount := decimal.NewFromFloat(detail.Amount) if pos[len(pos)-1].OpeningDirection != detail.Side { - amount := decimal.NewFromFloat(detail.Amount) value := decimal.NewFromFloat(detail.Price).Mul(amount) err = collateralReleaser.TakeProfit(amount, value, pos[len(pos)-1].RealisedPNL) if err != nil { return nil, err } + } else { + err = collateralReleaser.UpdateContracts(detail.Side, amount, decimal.Zero) + if err != nil { + return nil, err + } } err = p.UpdatePNL(f, f.GetClosePrice()) diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index b4aa203d1cb..19446673e29 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -59,7 +59,7 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc amount = portfolioSize } } - amount = amount.Round(8) + // amount = amount.Round(8) if amount.LessThanOrEqual(decimal.Zero) { return retOrder, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index 2ab4b380f95..ce57be1d4c6 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -80,7 +80,7 @@ func (o *Order) SetLeverage(l decimal.Decimal) { // GetAllocatedFunds returns the amount of funds the portfolio manager // has allocated to this potential position func (o *Order) GetAllocatedFunds() decimal.Decimal { - return o.AllocatedFunds + return o.AllocatedSize } // GetFillDependentEvent returns the fill dependent event diff --git a/backtester/eventtypes/order/order_test.go b/backtester/eventtypes/order/order_test.go index e41f26841ed..447e51dc511 100644 --- a/backtester/eventtypes/order/order_test.go +++ b/backtester/eventtypes/order/order_test.go @@ -77,7 +77,7 @@ func TestLeverage(t *testing.T) { func TestGetFunds(t *testing.T) { t.Parallel() o := Order{ - AllocatedFunds: decimal.NewFromInt(1337), + AllocatedSize: decimal.NewFromInt(1337), } funds := o.GetAllocatedFunds() if !funds.Equal(decimal.NewFromInt(1337)) { diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index c843a64e8b8..a5a8f5718cd 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -18,7 +18,7 @@ type Order struct { Amount decimal.Decimal OrderType order.Type Leverage decimal.Decimal - AllocatedFunds decimal.Decimal + AllocatedSize decimal.Decimal BuyLimit decimal.Decimal SellLimit decimal.Decimal FillDependentEvent signal.Event diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 793f318eb8b..e00311a8f34 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -502,13 +502,14 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { usd = latest.GetClosePrice() } } + if usd.IsZero() { + continue + } var side = gctorder.Buy if !f.items[i].available.GreaterThan(decimal.Zero) { side = gctorder.Sell } - if usd.IsZero() { - continue - } + butts.CollateralAssets = append(butts.CollateralAssets, gctorder.CollateralCalculator{ CalculateOffline: true, CollateralCurrency: f.items[i].currency, @@ -535,7 +536,6 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { f.items[i].asset == futureAsset && f.items[i].currency.Equal(futureCurrency) { f.items[i].available = collat.AvailableCollateral - f.items[i].reserved = collat.UsedCollateral return nil } } From a70231dd4226f8cce391d5536b0124c02db1f6a0 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 7 Mar 2022 15:35:18 +1100 Subject: [PATCH 088/171] Can render pnl results, focussing on funding statistics --- .../eventhandlers/portfolio/portfolio.go | 5 +- .../statistics/currencystatistics.go | 2 +- backtester/funding/collateral.go | 69 +++++++++++---- backtester/funding/funding.go | 87 +++++++++++++++---- backtester/funding/funding_test.go | 86 +++++++++--------- backtester/funding/funding_types.go | 30 +++---- exchanges/order/futures.go | 3 + exchanges/order/futures_types.go | 1 + 8 files changed, 185 insertions(+), 98 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index bc139faf9de..0978e38c03a 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -539,13 +539,12 @@ func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) } amount := decimal.NewFromFloat(detail.Amount) if pos[len(pos)-1].OpeningDirection != detail.Side { - value := decimal.NewFromFloat(detail.Price).Mul(amount) - err = collateralReleaser.TakeProfit(amount, value, pos[len(pos)-1].RealisedPNL) + err = collateralReleaser.TakeProfit(amount, pos[len(pos)-1].RealisedPNL) if err != nil { return nil, err } } else { - err = collateralReleaser.UpdateContracts(detail.Side, amount, decimal.Zero) + err = collateralReleaser.UpdateContracts(detail.Side, amount) if err != nil { return nil, err } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 047d2b5f513..230a8f2ea2f 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -210,7 +210,7 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. if last.PNL != nil { log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNLBeforeFees, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNL, 8, ".", ",")) } if len(errs) > 0 { log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Errors-------------------------------------") diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 682d669a813..03a6c5d97c1 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -23,12 +23,12 @@ func (c *Collateral) CanPlaceOrder(_ order.Side) bool { return c.Collateral.CanPlaceOrder() } -func (c *Collateral) TakeProfit(contracts, originalPositionSize, positionReturns decimal.Decimal) error { - err := c.Contract.Release(contracts, decimal.Zero) +func (c *Collateral) TakeProfit(contracts, positionReturns decimal.Decimal) error { + err := c.Contract.ReduceContracts(contracts) if err != nil { return err } - return c.Collateral.Release(originalPositionSize, positionReturns) + return c.Collateral.TakeProfit(positionReturns) } func (c *Collateral) ContractCurrency() currency.Code { @@ -60,32 +60,65 @@ func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { return c, nil } -func (c *Collateral) UpdateCollateral(s order.Side, amount, diff decimal.Decimal) error { +func (c *Collateral) UpdateCollateral(amount decimal.Decimal) error { + return c.Collateral.TakeProfit(amount) +} + +func (c *Collateral) UpdateContracts(s order.Side, amount decimal.Decimal) error { switch { case c.currentDirection == nil: c.currentDirection = &s - return c.Collateral.Reserve(amount) + return c.Contract.AddContracts(amount) case *c.currentDirection == s: - return c.Collateral.Reserve(amount) + return c.Contract.AddContracts(amount) case *c.currentDirection != s: - return c.Collateral.Release(amount, diff) + return c.Contract.ReduceContracts(amount) default: return errors.New("woah nelly") } } -func (c *Collateral) UpdateContracts(s order.Side, amount, diff decimal.Decimal) error { - switch { - case c.currentDirection == nil: - c.currentDirection = &s - return c.Contract.Reserve(amount) - case *c.currentDirection == s: - return c.Contract.Reserve(amount) - case *c.currentDirection != s: - return c.Contract.Release(amount, diff) - default: - return errors.New("woah nelly") +func (i *Item) TakeProfit(amount decimal.Decimal) error { + if !i.asset.IsFutures() { + return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) + } + i.available = i.available.Add(amount) + return nil +} + +// AddContracts allocates an amount of funds to be used at a later time +// it prevents multiple events from claiming the same resource +func (i *Item) AddContracts(amount decimal.Decimal) error { + if !i.asset.IsFutures() { + return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) + } + if amount.LessThanOrEqual(decimal.Zero) { + return errZeroAmountReceived + } + i.available = i.available.Add(amount) + return nil +} + +// ReduceContracts allocates an amount of funds to be used at a later time +// it prevents multiple events from claiming the same resource +func (i *Item) ReduceContracts(amount decimal.Decimal) error { + if !i.asset.IsFutures() { + return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) + } + if amount.LessThanOrEqual(decimal.Zero) { + return errZeroAmountReceived } + if amount.GreaterThan(i.available) { + return fmt.Errorf("%w for %v %v %v. Requested %v Reserved: %v", + errCannotAllocate, + i.exchange, + i.asset, + i.currency, + amount, + i.reserved) + } + i.available = i.available.Sub(amount) + return nil } func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index e00311a8f34..8af118642fc 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -122,10 +122,10 @@ func (f *FundManager) CreateSnapshot(t time.Time) { } if !f.disableUSDTracking { var usdClosePrice decimal.Decimal - if f.items[i].usdTrackingCandles == nil { + if f.items[i].trackingCandles == nil { continue } - usdCandles := f.items[i].usdTrackingCandles.GetStream() + usdCandles := f.items[i].trackingCandles.GetStream() for j := range usdCandles { if usdCandles[j].GetTime().Equal(t) { usdClosePrice = usdCandles[j].GetClosePrice() @@ -149,10 +149,6 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { if f.disableUSDTracking { return ErrUSDTrackingDisabled } - if k.Item.Asset.IsFutures() { - // futures doesn't need usd tracking? - return nil - } baseSet := false quoteSet := false var basePairedWith currency.Code @@ -160,23 +156,78 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { if baseSet && quoteSet { return nil } + if f.items[i].asset.IsFutures() && f.items[i].collateral && f.items[i].trackingCandles == nil { + usdCandles := gctkline.Item{ + Exchange: k.Item.Exchange, + Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, + Asset: k.Item.Asset, + Interval: k.Item.Interval, + Candles: make([]gctkline.Candle, len(k.Item.Candles)), + } + copy(usdCandles.Candles, k.Item.Candles) + for j := range usdCandles.Candles { + // usd stablecoins do not always match in value, + // this is a simplified implementation that can allow + // USD tracking for many currencies across many exchanges + // without retrieving n candle history and exchange rates + usdCandles.Candles[j].Open = 1 + usdCandles.Candles[j].High = 1 + usdCandles.Candles[j].Low = 1 + usdCandles.Candles[j].Close = 1 + } + cpy := *k + cpy.Item = usdCandles + if err := cpy.Load(); err != nil { + return err + } + f.items[i].trackingCandles = &cpy + quoteSet = true + continue + } + if strings.EqualFold(f.items[i].exchange, k.Item.Exchange) && f.items[i].asset == k.Item.Asset { if f.items[i].currency.Equal(k.Item.Pair.Base) { - if f.items[i].usdTrackingCandles == nil && + if f.items[i].trackingCandles == nil && trackingcurrencies.CurrencyIsUSDTracked(k.Item.Pair.Quote) { - f.items[i].usdTrackingCandles = k + f.items[i].trackingCandles = k if f.items[i].pairedWith != nil { basePairedWith = f.items[i].pairedWith.currency } } baseSet = true } + if f.items[i].asset.IsFutures() { + usdCandles := gctkline.Item{ + Exchange: k.Item.Exchange, + Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, + Asset: k.Item.Asset, + Interval: k.Item.Interval, + Candles: make([]gctkline.Candle, len(k.Item.Candles)), + } + copy(usdCandles.Candles, k.Item.Candles) + for j := range usdCandles.Candles { + // usd stablecoins do not always match in value, + // this is a simplified implementation that can allow + // USD tracking for many currencies across many exchanges + // without retrieving n candle history and exchange rates + usdCandles.Candles[j].Open = 1 + usdCandles.Candles[j].High = 1 + usdCandles.Candles[j].Low = 1 + usdCandles.Candles[j].Close = 1 + } + cpy := *k + cpy.Item = usdCandles + if err := cpy.Load(); err != nil { + return err + } + f.items[i].trackingCandles = &cpy + } if trackingcurrencies.CurrencyIsUSDTracked(f.items[i].currency) { if f.items[i].pairedWith != nil && !f.items[i].currency.Equal(basePairedWith) { continue } - if f.items[i].usdTrackingCandles == nil { + if f.items[i].trackingCandles == nil { usdCandles := gctkline.Item{ Exchange: k.Item.Exchange, Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, @@ -200,7 +251,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { if err := cpy.Load(); err != nil { return err } - f.items[i].usdTrackingCandles = &cpy + f.items[i].trackingCandles = &cpy } quoteSet = true } @@ -260,13 +311,13 @@ func (f *FundManager) GenerateReport() *Report { FinalFunds: f.items[i].available, } if !f.disableUSDTracking && - f.items[i].usdTrackingCandles != nil { - usdStream := f.items[i].usdTrackingCandles.GetStream() + f.items[i].trackingCandles != nil { + usdStream := f.items[i].trackingCandles.GetStream() item.USDInitialFunds = f.items[i].initialFunds.Mul(usdStream[0].GetClosePrice()) item.USDFinalFunds = f.items[i].available.Mul(usdStream[len(usdStream)-1].GetClosePrice()) item.USDInitialCostForOne = usdStream[0].GetClosePrice() item.USDFinalCostForOne = usdStream[len(usdStream)-1].GetClosePrice() - item.USDPairCandle = f.items[i].usdTrackingCandles + item.USDPairCandle = f.items[i].trackingCandles } var pricingOverTime []ItemSnapshot @@ -433,7 +484,7 @@ func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Cod func (f *FundManager) LiquidateByCollateral(c currency.Code) error { found := false for i := range f.items { - if f.items[i].currency == c && f.items[i].collateral && f.items[i].asset == asset.Futures { + if f.items[i].currency == c && !f.items[i].collateral && f.items[i].asset.IsFutures() { f.items[i].available = decimal.Zero f.items[i].reserved = decimal.Zero found = true @@ -459,8 +510,8 @@ func (f *FundManager) GetAllFunding() []BasicItem { var result []BasicItem for i := range f.items { var usd decimal.Decimal - if f.items[i].usdTrackingCandles != nil { - latest := f.items[i].usdTrackingCandles.Latest() + if f.items[i].trackingCandles != nil { + latest := f.items[i].trackingCandles.Latest() if latest != nil { usd = latest.GetClosePrice() } @@ -496,8 +547,8 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { exchMap[f.items[i].exchange] = exch } var usd decimal.Decimal - if f.items[i].usdTrackingCandles != nil { - latest := f.items[i].usdTrackingCandles.Latest() + if f.items[i].trackingCandles != nil { + latest := f.items[i].trackingCandles.Latest() if latest != nil { usd = latest.GetClosePrice() } diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index c7d353f0bbb..f118aefe2e8 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -211,32 +211,32 @@ func TestExists(t *testing.T) { // demonstration that you don't need the original *Items // to check for existence, just matching fields baseCopy := Item{ - exchange: baseItem.exchange, - asset: baseItem.asset, - currency: baseItem.currency, - initialFunds: baseItem.initialFunds, - available: baseItem.available, - reserved: baseItem.reserved, - transferFee: baseItem.transferFee, - pairedWith: baseItem.pairedWith, - usdTrackingCandles: baseItem.usdTrackingCandles, - snapshot: baseItem.snapshot, - collateral: baseItem.collateral, - collateralCandles: baseItem.collateralCandles, + exchange: baseItem.exchange, + asset: baseItem.asset, + currency: baseItem.currency, + initialFunds: baseItem.initialFunds, + available: baseItem.available, + reserved: baseItem.reserved, + transferFee: baseItem.transferFee, + pairedWith: baseItem.pairedWith, + trackingCandles: baseItem.trackingCandles, + snapshot: baseItem.snapshot, + collateral: baseItem.collateral, + collateralCandles: baseItem.collateralCandles, } quoteCopy := Item{ - exchange: quoteItem.exchange, - asset: quoteItem.asset, - currency: quoteItem.currency, - initialFunds: quoteItem.initialFunds, - available: quoteItem.available, - reserved: quoteItem.reserved, - transferFee: quoteItem.transferFee, - pairedWith: quoteItem.pairedWith, - usdTrackingCandles: quoteItem.usdTrackingCandles, - snapshot: quoteItem.snapshot, - collateral: quoteItem.collateral, - collateralCandles: quoteItem.collateralCandles, + exchange: quoteItem.exchange, + asset: quoteItem.asset, + currency: quoteItem.currency, + initialFunds: quoteItem.initialFunds, + available: quoteItem.available, + reserved: quoteItem.reserved, + transferFee: quoteItem.transferFee, + pairedWith: quoteItem.pairedWith, + trackingCandles: quoteItem.trackingCandles, + snapshot: quoteItem.snapshot, + collateral: quoteItem.collateral, + collateralCandles: quoteItem.collateralCandles, } quoteCopy.pairedWith = &baseCopy exists = f.Exists(&baseCopy) @@ -823,7 +823,7 @@ func TestGenerateReport(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - f.items[0].usdTrackingCandles = dfk + f.items[0].trackingCandles = dfk f.CreateSnapshot(dfk.Item.Candles[0].Time) report = f.GenerateReport() @@ -861,16 +861,16 @@ func TestCreateSnapshot(t *testing.T) { f := FundManager{} f.CreateSnapshot(time.Time{}) f.items = append(f.items, &Item{ - exchange: "", - asset: "", - currency: currency.EMPTYCODE, - initialFunds: decimal.Decimal{}, - available: decimal.Decimal{}, - reserved: decimal.Decimal{}, - transferFee: decimal.Decimal{}, - pairedWith: nil, - usdTrackingCandles: nil, - snapshot: nil, + exchange: "", + asset: "", + currency: currency.EMPTYCODE, + initialFunds: decimal.Decimal{}, + available: decimal.Decimal{}, + reserved: decimal.Decimal{}, + transferFee: decimal.Decimal{}, + pairedWith: nil, + trackingCandles: nil, + snapshot: nil, }) f.CreateSnapshot(time.Time{}) @@ -887,14 +887,14 @@ func TestCreateSnapshot(t *testing.T) { t.Error(err) } f.items = append(f.items, &Item{ - exchange: "test", - asset: asset.Spot, - currency: currency.BTC, - initialFunds: decimal.NewFromInt(1337), - available: decimal.NewFromInt(1337), - reserved: decimal.NewFromInt(1337), - transferFee: decimal.NewFromInt(1337), - usdTrackingCandles: dfk, + exchange: "test", + asset: asset.Spot, + currency: currency.BTC, + initialFunds: decimal.NewFromInt(1337), + available: decimal.NewFromInt(1337), + reserved: decimal.NewFromInt(1337), + transferFee: decimal.NewFromInt(1337), + trackingCandles: dfk, }) f.CreateSnapshot(dfk.Item.Candles[0].Time) } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 576405f5511..61bba84f126 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -116,27 +116,27 @@ type IPairReleaser interface { type ICollateralReleaser interface { ICollateralReader - UpdateCollateral(s order.Side, amount, diff decimal.Decimal) error - UpdateContracts(s order.Side, amount, diff decimal.Decimal) error - TakeProfit(contracts, originalPositionSize, positionReturns decimal.Decimal) error + UpdateCollateral(decimal.Decimal) error + UpdateContracts(order.Side, decimal.Decimal) error + TakeProfit(contracts, positionReturns decimal.Decimal) error ReleaseContracts(decimal.Decimal) error Liquidate() } // Item holds funding data per currency item type Item struct { - exchange string - asset asset.Item - currency currency.Code - initialFunds decimal.Decimal - available decimal.Decimal - reserved decimal.Decimal - transferFee decimal.Decimal - pairedWith *Item - usdTrackingCandles *kline.DataFromKline - snapshot map[time.Time]ItemSnapshot - collateral bool - collateralCandles map[currency.Code]kline.DataFromKline + exchange string + asset asset.Item + currency currency.Code + initialFunds decimal.Decimal + available decimal.Decimal + reserved decimal.Decimal + transferFee decimal.Decimal + pairedWith *Item + trackingCandles *kline.DataFromKline + snapshot map[time.Time]ItemSnapshot + collateral bool + collateralCandles map[currency.Code]kline.DataFromKline } // BasicItem is a representation of Item diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 57f132c151f..2175de17f29 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -351,6 +351,7 @@ func (p *PositionTracker) GetStats() PositionStats { var orders []Detail orders = append(orders, p.longPositions...) orders = append(orders, p.shortPositions...) + return PositionStats{ Exchange: p.exchange, Asset: p.asset, @@ -597,6 +598,8 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.closingPrice = decimal.NewFromFloat(d.Price) p.realisedPNL = calculateRealisedPNL(p.pnlHistory) p.unrealisedPNL = decimal.Zero + p.pnlHistory[len(p.pnlHistory)-1].RealisedPNL = p.realisedPNL + p.pnlHistory[len(p.pnlHistory)-1].UnrealisedPNL = p.unrealisedPNL } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index f9478a7ade8..040d10d79fa 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -263,6 +263,7 @@ type PNLResult struct { Time time.Time UnrealisedPNL decimal.Decimal RealisedPNLBeforeFees decimal.Decimal + RealisedPNL decimal.Decimal Price decimal.Decimal Exposure decimal.Decimal Fee decimal.Decimal From 0ff3b32b10dc45627965372edb8aabe5a381e00e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 8 Mar 2022 10:19:06 +1100 Subject: [PATCH 089/171] tracking candles for futures, but why not btc --- backtester/funding/funding.go | 75 +++++++++++------------------ backtester/funding/funding_test.go | 4 +- backtester/funding/funding_types.go | 2 +- 3 files changed, 30 insertions(+), 51 deletions(-) diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 8af118642fc..78c0b8308b3 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -94,11 +94,11 @@ func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) err } } collateral := &Item{ - exchange: item.exchange, - asset: item.asset, - currency: code, - pairedWith: item, - collateral: true, + exchange: item.exchange, + asset: item.asset, + currency: code, + pairedWith: item, + isCollateral: true, } err := f.AddItem(collateral) if err != nil { @@ -156,48 +156,8 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { if baseSet && quoteSet { return nil } - if f.items[i].asset.IsFutures() && f.items[i].collateral && f.items[i].trackingCandles == nil { - usdCandles := gctkline.Item{ - Exchange: k.Item.Exchange, - Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, - Asset: k.Item.Asset, - Interval: k.Item.Interval, - Candles: make([]gctkline.Candle, len(k.Item.Candles)), - } - copy(usdCandles.Candles, k.Item.Candles) - for j := range usdCandles.Candles { - // usd stablecoins do not always match in value, - // this is a simplified implementation that can allow - // USD tracking for many currencies across many exchanges - // without retrieving n candle history and exchange rates - usdCandles.Candles[j].Open = 1 - usdCandles.Candles[j].High = 1 - usdCandles.Candles[j].Low = 1 - usdCandles.Candles[j].Close = 1 - } - cpy := *k - cpy.Item = usdCandles - if err := cpy.Load(); err != nil { - return err - } - f.items[i].trackingCandles = &cpy - quoteSet = true - continue - } - - if strings.EqualFold(f.items[i].exchange, k.Item.Exchange) && - f.items[i].asset == k.Item.Asset { - if f.items[i].currency.Equal(k.Item.Pair.Base) { - if f.items[i].trackingCandles == nil && - trackingcurrencies.CurrencyIsUSDTracked(k.Item.Pair.Quote) { - f.items[i].trackingCandles = k - if f.items[i].pairedWith != nil { - basePairedWith = f.items[i].pairedWith.currency - } - } - baseSet = true - } - if f.items[i].asset.IsFutures() { + if f.items[i].asset.IsFutures() { + if f.items[i].isCollateral { usdCandles := gctkline.Item{ Exchange: k.Item.Exchange, Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, @@ -222,6 +182,25 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { return err } f.items[i].trackingCandles = &cpy + quoteSet = true + } else { + f.items[i].trackingCandles = k + baseSet = true + } + continue + } + + if strings.EqualFold(f.items[i].exchange, k.Item.Exchange) && + f.items[i].asset == k.Item.Asset { + if f.items[i].currency.Equal(k.Item.Pair.Base) { + if f.items[i].trackingCandles == nil && + trackingcurrencies.CurrencyIsUSDTracked(k.Item.Pair.Quote) { + f.items[i].trackingCandles = k + if f.items[i].pairedWith != nil { + basePairedWith = f.items[i].pairedWith.currency + } + } + baseSet = true } if trackingcurrencies.CurrencyIsUSDTracked(f.items[i].currency) { if f.items[i].pairedWith != nil && !f.items[i].currency.Equal(basePairedWith) { @@ -484,7 +463,7 @@ func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Cod func (f *FundManager) LiquidateByCollateral(c currency.Code) error { found := false for i := range f.items { - if f.items[i].currency == c && !f.items[i].collateral && f.items[i].asset.IsFutures() { + if f.items[i].currency == c && !f.items[i].isCollateral && f.items[i].asset.IsFutures() { f.items[i].available = decimal.Zero f.items[i].reserved = decimal.Zero found = true diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index f118aefe2e8..01b0ac5a1ed 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -221,7 +221,7 @@ func TestExists(t *testing.T) { pairedWith: baseItem.pairedWith, trackingCandles: baseItem.trackingCandles, snapshot: baseItem.snapshot, - collateral: baseItem.collateral, + isCollateral: baseItem.isCollateral, collateralCandles: baseItem.collateralCandles, } quoteCopy := Item{ @@ -235,7 +235,7 @@ func TestExists(t *testing.T) { pairedWith: quoteItem.pairedWith, trackingCandles: quoteItem.trackingCandles, snapshot: quoteItem.snapshot, - collateral: quoteItem.collateral, + isCollateral: quoteItem.isCollateral, collateralCandles: quoteItem.collateralCandles, } quoteCopy.pairedWith = &baseCopy diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 61bba84f126..d9c4c855e3a 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -135,7 +135,7 @@ type Item struct { pairedWith *Item trackingCandles *kline.DataFromKline snapshot map[time.Time]ItemSnapshot - collateral bool + isCollateral bool collateralCandles map[currency.Code]kline.DataFromKline } From 69bd71f8aa716a9496e24d0586aa67c3b1f2a6a5 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 8 Mar 2022 16:39:41 +1100 Subject: [PATCH 090/171] Improves funding statistics --- backtester/engine/backtest.go | 15 ++- backtester/eventhandlers/exchange/exchange.go | 6 + .../statistics/fundingstatistics.go | 127 +++++++++++++----- .../statistics/statistics_types.go | 13 ++ .../ftxcashandcarry/ftxcashandcarry.go | 35 ++--- backtester/funding/funding.go | 7 +- backtester/funding/funding_types.go | 10 +- 7 files changed, 150 insertions(+), 63 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index a5c7b88720f..1795d7a6480 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -7,6 +7,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" @@ -128,7 +129,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { case fill.Event: bt.processFillEvent(eType, funds.FundReleaser()) default: - return fmt.Errorf("%w %T received, could not process", + return fmt.Errorf("handleEvent %w %T received, could not process", errUnhandledDatatype, ev) } @@ -148,12 +149,12 @@ func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds fun // too much bad data is a severe error and backtesting must cease return err } - log.Error(common.SubLoggers[common.Backtester], err) + log.Errorf(common.SubLoggers[common.Backtester], "OnSignal %v", err) return nil } err = bt.Statistic.SetEventForOffset(s) if err != nil { - log.Error(common.SubLoggers[common.Backtester], err) + log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v", err) } bt.EventQueue.AppendEvent(s) @@ -276,10 +277,12 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease f, err := bt.Exchange.ExecuteOrder(ev, d, bt.orderManager, funds) if err != nil { if f == nil { - log.Errorf(common.SubLoggers[common.Backtester], "fill event should always be returned, please fix, %v", err) + log.Errorf(common.SubLoggers[common.Backtester], "ExecuteOrder fill event should always be returned, please fix, %v", err) return } - log.Errorf(common.SubLoggers[common.Backtester], "%v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) + if !errors.Is(err, exchange.ErrDoNothing) { + log.Errorf(common.SubLoggers[common.Backtester], "ExecuteOrder %v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) + } } err = bt.Statistic.SetEventForOffset(f) if err != nil { @@ -306,7 +309,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Error(common.SubLoggers[common.Backtester], err) } if holding == nil { - log.Error(common.SubLoggers[common.Backtester], "why is holdings nill?") + log.Error(common.SubLoggers[common.Backtester], "ViewHoldingAtTimePeriod why is holdings nill?") } else { err = bt.Statistic.AddHoldingsForTime(holding) if err != nil { diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index ec0ece7b5b8..e833303e425 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -2,6 +2,7 @@ package exchange import ( "context" + "errors" "fmt" "strings" @@ -26,6 +27,8 @@ func (e *Exchange) Reset() { *e = Exchange{} } +var ErrDoNothing = errors.New("received Do Nothing direction") + // ExecuteOrder assesses the portfolio manager's order event and if it passes validation // will send an order to the exchange/fake order manager to be stored and raise a fill event func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager *engine.OrderManager, funds funding.IFundReleaser) (fill.Event, error) { @@ -44,6 +47,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ClosePrice: data.Latest().GetClosePrice(), FillDependentEvent: o.GetFillDependentEvent(), } + if o.GetDirection() == common.DoNothing { + return f, ErrDoNothing + } if o.GetAssetType().IsFutures() && !o.IsClosingPosition() { f.Amount = o.GetAllocatedFunds() } diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index ea6dc34870f..862118a7f79 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -58,9 +58,11 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str RiskFreeRate: riskFreeRate, } for i := range response.Items { - usdStats.TotalOrders += response.Items[i].TotalOrders - usdStats.BuyOrders += response.Items[i].BuyOrders - usdStats.SellOrders += response.Items[i].SellOrders + if !response.Items[i].IsCollateral { + usdStats.TotalOrders += response.Items[i].TotalOrders + usdStats.BuyOrders += response.Items[i].BuyOrders + usdStats.SellOrders += response.Items[i].SellOrders + } } for k, v := range report.USDTotalsOverTime { if usdStats.HighestHoldingValue.Value.LessThan(v.USDValue) { @@ -176,17 +178,33 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f item.HighestClosePrice.Time = closePrices[i].Time } } - for i := range relatedStats { - if relatedStats[i].stat == nil { - return nil, fmt.Errorf("%w related stats", common.ErrNilArguments) - } - if relatedStats[i].isBaseCurrency { - item.BuyOrders += relatedStats[i].stat.BuyOrders - item.SellOrders += relatedStats[i].stat.SellOrders + item.IsCollateral = reportItem.IsCollateral + if item.IsCollateral { + //item.LowestCollateral = something() + //item.HighestCollateral = something() + //item.EndingCollateral = something() + //item.InitialCollateral = something() + } else if reportItem.Asset.IsFutures() { + // item.HighestRPNL = somethingElse() + // item.LowestRPNL = somethingElse() + // item.FinalUPNL = somethingElse() + // item.FinalRPNL = somethingElse() + // item.HighestUPNL = somethingElse() + // item.LowestUPNL = somethingElse() + } + if !reportItem.IsCollateral { + for i := range relatedStats { + if relatedStats[i].stat == nil { + return nil, fmt.Errorf("%w related stats", common.ErrNilArguments) + } + if relatedStats[i].isBaseCurrency { + item.BuyOrders += relatedStats[i].stat.BuyOrders + item.SellOrders += relatedStats[i].stat.SellOrders + } } } item.TotalOrders = item.BuyOrders + item.SellOrders - if !item.ReportItem.ShowInfinite { + if !item.ReportItem.ShowInfinite && !reportItem.IsCollateral { if item.ReportItem.Snapshots[0].USDValue.IsZero() { item.ReportItem.ShowInfinite = true } else { @@ -203,7 +221,9 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f item.ReportItem.Snapshots[0].USDClosePrice).Mul( decimal.NewFromInt(100)) } - item.DidStrategyBeatTheMarket = item.StrategyMovement.GreaterThan(item.MarketMovement) + if !reportItem.IsCollateral { + item.DidStrategyBeatTheMarket = item.StrategyMovement.GreaterThan(item.MarketMovement) + } item.HighestCommittedFunds = ValueAtTime{} for j := range item.ReportItem.Snapshots { if item.ReportItem.Snapshots[j].USDValue.GreaterThan(item.HighestCommittedFunds.Value) { @@ -220,12 +240,12 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f if len(s) == 0 { return nil, fmt.Errorf("%w stream missing", errMissingSnapshots) } + if reportItem.IsCollateral { + return item, nil + } var err error item.MaxDrawdown, err = CalculateBiggestEventDrawdown(s) - if err != nil { - return nil, err - } - return item, nil + return item, err } // PrintResults outputs all calculated funding statistics to the command line @@ -233,28 +253,65 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { if f.Report == nil { return fmt.Errorf("%w requires report to be generated", common.ErrNilArguments) } - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding------------------------------------") - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding Item Results-----------------------") - for i := range f.Report.Items { - sep := fmt.Sprintf("%v %v %v |\t", f.Report.Items[i].Exchange, f.Report.Items[i].Asset, f.Report.Items[i].Currency) - if !f.Report.Items[i].PairedWith.IsEmpty() { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Paired with: %v", sep, f.Report.Items[i].PairedWith) - } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].InitialFunds, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].FinalFunds, 8, ".", ",")) - if !f.Report.DisableUSDTracking && f.Report.UsingExchangeLevelFunding { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].USDInitialFunds, 2, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].USDFinalFunds, 2, ".", ",")) - } - if f.Report.Items[i].ShowInfinite { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: ∞%%", sep) + var spotResults, futuresResults []FundingItemStatistics + for i := range f.Items { + if f.Items[i].ReportItem.Asset.IsFutures() { + futuresResults = append(futuresResults, f.Items[i]) } else { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].Difference, 8, ".", ",")) + spotResults = append(spotResults, f.Items[i]) } - if f.Report.Items[i].TransferFee.GreaterThan(decimal.Zero) { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(f.Report.Items[i].TransferFee, 8, ".", ",")) + } + if len(spotResults) > 0 || len(futuresResults) > 0 { + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding------------------------------------") + } + if len(spotResults) > 0 { + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding Spot Item Results------------------") + for i := range spotResults { + sep := fmt.Sprintf("%v %v %v |\t", spotResults[i].ReportItem.Exchange, spotResults[i].ReportItem.Asset, spotResults[i].ReportItem.Currency) + if !spotResults[i].ReportItem.PairedWith.IsEmpty() { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Paired with: %v", sep, spotResults[i].ReportItem.PairedWith) + } + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.InitialFunds, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.FinalFunds, 8, ".", ",")) + + if !f.Report.DisableUSDTracking && f.Report.UsingExchangeLevelFunding { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDInitialFunds, 2, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDFinalFunds, 2, ".", ",")) + } + if spotResults[i].ReportItem.ShowInfinite { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: ∞%%", sep) + } else { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.Difference, 8, ".", ",")) + } + if spotResults[i].ReportItem.TransferFee.GreaterThan(decimal.Zero) { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.TransferFee, 8, ".", ",")) + } + log.Info(common.SubLoggers[common.FundingStatistics], "") + } + } + if len(futuresResults) > 0 { + log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding Futures Item Results---------------") + for i := range futuresResults { + sep := fmt.Sprintf("%v %v %v |\t", futuresResults[i].ReportItem.Exchange, futuresResults[i].ReportItem.Asset, futuresResults[i].ReportItem.Currency) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Is Collateral: %v", sep, futuresResults[i].IsCollateral) + if futuresResults[i].IsCollateral { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial Collateral: %v %v at %v", sep, futuresResults[i].InitialCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialCollateral.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Collateral: %v %v at %v", sep, futuresResults[i].EndingCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].EndingCollateral.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].LowestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestCollateral.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].HighestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestCollateral.Time) + } else { + if !futuresResults[i].ReportItem.PairedWith.IsEmpty() { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Collateral currency: %v", sep, futuresResults[i].ReportItem.PairedWith) + } + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Unrealised PNL: %v %v at %v", sep, futuresResults[i].LowestUPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].LowestUPNL.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Unrealised PNL: %v %v at %v", sep, futuresResults[i].HighestUPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].HighestUPNL.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Realised PNL: %v %v at %v", sep, futuresResults[i].LowestRPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].LowestRPNL.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Realised PNL: %v %v at %v", sep, futuresResults[i].HighestRPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].HighestRPNL.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Unrealised PNL: %v %v at %v", sep, futuresResults[i].FinalUPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].FinalUPNL.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Realised PNL: %v %v at %v", sep, futuresResults[i].FinalRPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].FinalRPNL.Time) + } + log.Info(common.SubLoggers[common.FundingStatistics], "") } - log.Info(common.SubLoggers[common.FundingStatistics], "") } if f.Report.DisableUSDTracking { return nil diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index c4962354986..8f1379b5c69 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -217,6 +217,19 @@ type FundingItemStatistics struct { TotalOrders int64 MaxDrawdown Swing HighestCommittedFunds ValueAtTime + // Collateral stats + IsCollateral bool + InitialCollateral ValueAtTime + EndingCollateral ValueAtTime + HighestCollateral ValueAtTime + LowestCollateral ValueAtTime + // PNL + LowestUPNL ValueAtTime + HighestUPNL ValueAtTime + LowestRPNL ValueAtTime + HighestRPNL ValueAtTime + FinalUPNL ValueAtTime + FinalRPNL ValueAtTime } // TotalFundingStatistics holds values for overal statistics for funding items diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 73312b6f152..147147ebd30 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -57,7 +57,6 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if err != nil { return nil, err } - for _, v := range sortedSignals { pos, err := p.GetPositions(v.futureSignal.Latest()) if err != nil { @@ -72,6 +71,8 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf return nil, err } + spotSignal.SetDirection(common.DoNothing) + futuresSignal.SetDirection(common.DoNothing) fp := v.futureSignal.Latest().GetClosePrice() sp := v.spotSignal.Latest().GetClosePrice() switch { @@ -91,25 +92,27 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf // as the futures signal relies on a completed spot order purchase // to use as collateral spotSignal.FillDependentEvent = &futuresSignal - response = append(response, &spotSignal) case len(pos) > 0 && v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position on last event") - response = append(response, &futuresSignal) - case len(pos) > 0 && pos[len(pos)-1].Status == order.Open: - if fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage) { - futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReason("closing position after reaching close short distance percentage") - response = append(response, &futuresSignal) - } - case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed: - if fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage) { - futuresSignal.SetDirection(order.Short) - futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) - futuresSignal.AppendReason("opening position after reaching open short distance percentage") - response = append(response, &futuresSignal) - } + spotSignal.AppendReason("no action required") + case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && + fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage): + futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.AppendReason("closing position after reaching close short distance percentage") + spotSignal.AppendReason("no action required") + case len(pos) > 0 && + pos[len(pos)-1].Status == order.Closed && + fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage): + futuresSignal.SetDirection(order.Short) + futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) + futuresSignal.AppendReason("opening position after reaching open short distance percentage") + spotSignal.AppendReason("no action required") + default: + futuresSignal.AppendReason("no action required") + spotSignal.AppendReason("no action required") } + response = append(response, &spotSignal, &futuresSignal) } return response, nil } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 78c0b8308b3..17fc4664fc3 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -156,7 +156,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { if baseSet && quoteSet { return nil } - if f.items[i].asset.IsFutures() { + if f.items[i].asset.IsFutures() && k.Item.Asset.IsFutures() { if f.items[i].isCollateral { usdCandles := gctkline.Item{ Exchange: k.Item.Exchange, @@ -288,6 +288,7 @@ func (f *FundManager) GenerateReport() *Report { InitialFunds: f.items[i].initialFunds, TransferFee: f.items[i].transferFee, FinalFunds: f.items[i].available, + IsCollateral: f.items[i].isCollateral, } if !f.disableUSDTracking && f.items[i].trackingCandles != nil { @@ -517,6 +518,10 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { } for i := range f.items { + if !f.items[i].isCollateral && f.items[i].asset.IsFutures() { + // contracts don't contribute to collateral + continue + } exch, ok := exchMap[f.items[i].exchange] if !ok { exch, err = f.exchangeManager.GetExchangeByName(f.items[i].exchange) diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index d9c4c855e3a..f34d5b9ef4b 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -185,11 +185,11 @@ type ReportItem struct { USDFinalFunds decimal.Decimal USDFinalCostForOne decimal.Decimal Snapshots []ItemSnapshot - - USDPairCandle *kline.DataFromKline - Difference decimal.Decimal - ShowInfinite bool - PairedWith currency.Code + USDPairCandle *kline.DataFromKline + Difference decimal.Decimal + ShowInfinite bool + PairedWith currency.Code + IsCollateral bool } // ItemSnapshot holds USD values to allow for tracking From 9a7d084d20d4617832d6bc399d63a80d757bd5ec Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 9 Mar 2022 17:37:07 +1100 Subject: [PATCH 091/171] Colours and stats --- backtester/common/common.go | 30 +- backtester/common/common_types.go | 95 +++-- backtester/config/config.go | 35 +- backtester/engine/backtest.go | 2 +- backtester/eventhandlers/statistics/common.go | 305 ++++++++++++++ .../statistics/currencystatistics.go | 300 +------------- .../statistics/fundingstatistics.go | 162 ++------ .../eventhandlers/statistics/printresults.go | 375 ++++++++++++++++++ .../eventhandlers/statistics/statistics.go | 223 ----------- .../statistics/statistics_types.go | 21 +- .../ftxcashandcarry/ftxcashandcarry.go | 6 - backtester/main.go | 38 +- 12 files changed, 878 insertions(+), 714 deletions(-) create mode 100644 backtester/eventhandlers/statistics/common.go create mode 100644 backtester/eventhandlers/statistics/printresults.go diff --git a/backtester/common/common.go b/backtester/common/common.go index ccb5c4d2bad..c6d2154ef02 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -1,6 +1,9 @@ package common -import "fmt" +import ( + "fmt" + "strings" +) // DataTypeToInt converts the config string value into an int func DataTypeToInt(dataType string) (int64, error) { @@ -13,3 +16,28 @@ func DataTypeToInt(dataType string) (int64, error) { return 0, fmt.Errorf("unrecognised dataType '%v'", dataType) } } + +// FitStringInLimit ensures a string is within the limit +// it also helps elongate a string to fit the limit +func FitStringInLimit(str, spacer string, limit int, upper bool) string { + limResp := limit - len(str) + if upper { + str = strings.ToUpper(str) + } + if limResp < 0 { + return str[0:limit-3] + "..." + } + spacerLen := len(spacer) + for i := 0; i < limResp; i++ { + str = str + spacer + for j := 0; j < spacerLen; j++ { + if j > 0 { + // prevent clever people from going beyond + // the limit by having a spacer longer than 1 + i++ + } + } + } + + return str +} diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 96d6a19e75b..7d66ace84fd 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -12,6 +12,13 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) +const ( + // CandleStr is a config readable data type to tell the backtester to retrieve candle data + CandleStr = "candle" + // TradeStr is a config readable data type to tell the backtester to retrieve trade data + TradeStr = "trade" +) + const ( // DoNothing is an explicit signal for the backtester to not perform an action // based upon indicator results @@ -30,18 +37,13 @@ const ( // of any exposure of a position // This will handle any amount of exposure, no need to calculate how // much to close - ClosePosition order.Side = "CLOSE POSITION" - + ClosePosition order.Side = "CLOSE POSITION" CouldNotCloseShort order.Side = "COULD NOT CLOSE SHORT" CouldNotCloseLong order.Side = "COULD NOT CLOSE LONG" Liquidated order.Side = "LIQUIDATED" // MissingData is signalled during the strategy/signal phase when data has been identified as missing // No buy or sell events can occur MissingData order.Side = "MISSING DATA" - // CandleStr is a config readable data type to tell the backtester to retrieve candle data - CandleStr = "candle" - // TradeStr is a config readable data type to tell the backtester to retrieve trade data - TradeStr = "trade" ) // DataCandle is an int64 representation of a candle data type @@ -136,27 +138,70 @@ type Directioner interface { GetDirection() order.Side } +// colours to display for the terminal output +// colours to display for the terminal output +var ( + ColourDefault = "\u001b[0m" + ColourGreen = "\033[38;5;157m" + ColourWhite = "\033[38;5;255m" + ColourGrey = "\033[38;5;240m" + ColourDarkGrey = "\033[38;5;243m" + ColourH1 = "\033[38;5;33m" + ColourH2 = "\033[38;5;39m" + ColourH3 = "\033[38;5;45m" + ColourH4 = "\033[38;5;51m" + ColourSuccess = "\033[38;5;40m" + ColourInfo = "\u001B[32m" + ColourDebug = "\u001B[34m" + ColourWarn = "\u001B[33m" + ColourProblem = "\033[38;5;251m" + ColourError = "\033[38;5;196m" +) + +var ( + logo01 = " " + logo02 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@ " + logo03 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + ",,,,,," + ColourWhite + " " + logo04 = " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,,,,,," + ColourWhite + " " + logo05 = " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " " + logo06 = " " + ColourWhite + "@@@@@@" + ColourGrey + "(,,,,,,,, " + ColourGrey + ",," + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " " + logo07 = " " + ColourGrey + ",," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, #,,,,,,,,,,,,,,,,,," + ColourWhite + " " + logo08 = " " + ColourGrey + ",,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,,,,,,,,,,,,,," + ColourGreen + "%%%%%%%" + ColourWhite + " " + logo09 = " " + ColourGrey + ",,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,," + ColourGreen + "%%%%%" + ColourGrey + " ,,,,,," + ColourGrey + "%" + ColourGreen + "%%%%%%" + ColourWhite + " " + logo10 = " " + ColourGrey + ",,,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,," + ColourGreen + "%%%%%%%%%%%%%%%%%%" + ColourGrey + "#" + ColourGreen + "%%" + ColourGrey + " " + logo11 = " " + ColourGrey + ",,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,," + ColourGreen + "%%%" + ColourGrey + " ,,,,," + ColourGreen + "%%%%%%%%" + ColourGrey + ",,,,, " + logo12 = " " + ColourGrey + ",,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourGreen + "%%" + ColourGrey + ",, ,,,,,,," + ColourWhite + "@" + ColourGreen + "*%%," + ColourWhite + "@" + ColourGrey + ",,,,,, " + logo13 = " " + ColourGrey + "*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourGrey + ",,,,," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " " + logo14 = " " + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " " + logo15 = " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " " + logo16 = " " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + "#,,,,,,," + ColourWhite + " " + logo17 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + "*,,,," + ColourWhite + " " + logo18 = " " + ColourWhite + "@@@@@@@@@@@@@@@@" + ColourDefault + " " + + LogoLines = []string{ + logo01, + logo02, + logo03, + logo04, + logo05, + logo06, + logo07, + logo08, + logo09, + logo10, + logo11, + logo12, + logo13, + logo14, + logo15, + logo16, + logo17, + logo18, + } +) + // ASCIILogo is a sweet logo that is optionally printed to the command line window const ASCIILogo = ` - - @@@@@@@@@@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@ ,,,,,, - @@@@@@@@,,,,, @@@@@@@@@,,,,,,,, - @@@@@@@@,,,,,,, @@@@@@@,,,,,,, - @@@@@@(,,,,,,,, ,,@@@@@@@,,,,,, - ,,@@@@@@,,,,,,,,, #,,,,,,,,,,,,,,,,, - ,,,,*@@@@@@,,,,,,,,,,,,,,,,,,,,,,,,,,%%%%%%% - ,,,,,,,*@@@@@@,,,,,,,,,,,,,,%%%%%,,,,,,%%%%%%%% - ,,,,,,,,*@@@@@@,,,,,,,,,,,%%%%%%%%%%%%%%%%%%#%% - ,,,,,,*@@@@@@,,,,,,,,,%%%,,,,,%%%%%%%%,,,,, - ,,,*@@@@@@,,,,,,%%, ,,,,,,,@*%%,@,,,,,, - *@@@@@@,,,,,,,,, ,,,,@@@@@@,,,,,, - @@@@@@,,,,,,,,, @@@@@@@,,,,,, - @@@@@@@@,,,,,,, @@@@@@@,,,,,,, - @@@@@@@@@,,,, @@@@@@@@@#,,,,,,, - @@@@@@@@@@@@@@@@@@@@@@@ *,,,, - @@@@@@@@@@@@@@@@ - ______ ______ __ ______ __ / ____/___ / ____/______ ______ / /_____/_ __/________ _____/ /__ _____ / / __/ __ \/ / / ___/ / / / __ \/ __/ __ \/ / / ___/ __ / __ / _ \/ ___/ diff --git a/backtester/config/config.go b/backtester/config/config.go index 53603a12b6b..0603ba6b2f6 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -39,11 +39,8 @@ func LoadConfig(data []byte) (resp *Config, err error) { // PrintSetting prints relevant settings to the console for easy reading func (c *Config) PrintSetting() { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------Backtester Settings------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------Strategy Settings--------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH1+"------------------Backtester Settings------------------------"+common.ColourDefault) + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Strategy Settings--------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Config], "Strategy: %s", c.StrategySettings.Name) if len(c.StrategySettings.CustomSettings) > 0 { log.Info(common.SubLoggers[common.Config], "Custom strategy variables:") @@ -57,8 +54,7 @@ func (c *Config) PrintSetting() { log.Infof(common.SubLoggers[common.Config], "Use Exchange Level Funding: %v", c.StrategySettings.UseExchangeLevelFunding) log.Infof(common.SubLoggers[common.Config], "USD value tracking: %v", !c.StrategySettings.DisableUSDTracking) if c.StrategySettings.UseExchangeLevelFunding && c.StrategySettings.SimultaneousSignalProcessing { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------Funding Settings---------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Funding Settings---------------------------"+common.ColourDefault) for i := range c.StrategySettings.ExchangeLevelFunding { log.Infof(common.SubLoggers[common.Config], "Initial funds for %v %v %v: %v", c.StrategySettings.ExchangeLevelFunding[i].ExchangeName, @@ -69,13 +65,11 @@ func (c *Config) PrintSetting() { } for i := range c.CurrencySettings { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - currStr := fmt.Sprintf("------------------%v %v-%v Currency Settings---------------------------------------------------------", + currStr := fmt.Sprintf(common.ColourH2+"------------------%v %v-%v Currency Settings---------------------------------------------------------"+common.ColourDefault, c.CurrencySettings[i].Asset, c.CurrencySettings[i].Base, c.CurrencySettings[i].Quote) log.Infof(common.SubLoggers[common.Config], currStr[:61]) - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") log.Infof(common.SubLoggers[common.Config], "Exchange: %v", c.CurrencySettings[i].ExchangeName) if !c.StrategySettings.UseExchangeLevelFunding && c.CurrencySettings[i].SpotDetails != nil { if c.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { @@ -101,48 +95,37 @@ func (c *Config) PrintSetting() { log.Infof(common.SubLoggers[common.Config], "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) } - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------Portfolio Settings-------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Portfolio Settings-------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Config], "Buy rules: %+v", c.PortfolioSettings.BuySide) log.Infof(common.SubLoggers[common.Config], "Sell rules: %+v", c.PortfolioSettings.SellSide) log.Infof(common.SubLoggers[common.Config], "Leverage rules: %+v", c.PortfolioSettings.Leverage) if c.DataSettings.LiveData != nil { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------Live Settings------------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Live Settings------------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) log.Infof(common.SubLoggers[common.Config], "REAL ORDERS: %v", c.DataSettings.LiveData.RealOrders) log.Infof(common.SubLoggers[common.Config], "Overriding GCT API settings: %v", c.DataSettings.LiveData.APIClientIDOverride != "") } if c.DataSettings.APIData != nil { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------API Settings-------------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------API Settings-------------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) log.Infof(common.SubLoggers[common.Config], "Start date: %v", c.DataSettings.APIData.StartDate.Format(gctcommon.SimpleTimeFormat)) log.Infof(common.SubLoggers[common.Config], "End date: %v", c.DataSettings.APIData.EndDate.Format(gctcommon.SimpleTimeFormat)) } if c.DataSettings.CSVData != nil { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------CSV Settings-------------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------CSV Settings-------------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) log.Infof(common.SubLoggers[common.Config], "CSV file: %v", c.DataSettings.CSVData.FullPath) } if c.DataSettings.DatabaseData != nil { - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") - log.Info(common.SubLoggers[common.Config], "------------------Database Settings--------------------------") - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------") + log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Database Settings--------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) log.Infof(common.SubLoggers[common.Config], "Start date: %v", c.DataSettings.DatabaseData.StartDate.Format(gctcommon.SimpleTimeFormat)) log.Infof(common.SubLoggers[common.Config], "End date: %v", c.DataSettings.DatabaseData.EndDate.Format(gctcommon.SimpleTimeFormat)) } - log.Info(common.SubLoggers[common.Config], "-------------------------------------------------------------\n\n") } // Validate checks all config settings diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 1795d7a6480..ae2e1f64227 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -309,7 +309,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Error(common.SubLoggers[common.Backtester], err) } if holding == nil { - log.Error(common.SubLoggers[common.Backtester], "ViewHoldingAtTimePeriod why is holdings nill?") + log.Error(common.SubLoggers[common.Backtester], "ViewHoldingAtTimePeriod why is holdings nil?") } else { err = bt.Statistic.AddHoldingsForTime(holding) if err != nil { diff --git a/backtester/eventhandlers/statistics/common.go b/backtester/eventhandlers/statistics/common.go new file mode 100644 index 00000000000..c007134cc4e --- /dev/null +++ b/backtester/eventhandlers/statistics/common.go @@ -0,0 +1,305 @@ +package statistics + +import ( + "errors" + "fmt" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + gctmath "github.com/thrasher-corp/gocryptotrader/common/math" + gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/log" +) + +// fSIL shorthand wrapper for FitStringInLimit +func fSIL(str string, limit int) string { + spacer := " " + return common.FitStringInLimit(str, spacer, limit, true) +} + +// CalculateBiggestEventDrawdown calculates the biggest drawdown using a slice of DataEvents +func CalculateBiggestEventDrawdown(closePrices []common.DataEventHandler) (Swing, error) { + if len(closePrices) == 0 { + return Swing{}, fmt.Errorf("%w to calculate drawdowns", errReceivedNoData) + } + var swings []Swing + lowestPrice := closePrices[0].GetLowPrice() + highestPrice := closePrices[0].GetHighPrice() + lowestTime := closePrices[0].GetTime() + highestTime := closePrices[0].GetTime() + interval := closePrices[0].GetInterval() + + for i := range closePrices { + currHigh := closePrices[i].GetHighPrice() + currLow := closePrices[i].GetLowPrice() + currTime := closePrices[i].GetTime() + if lowestPrice.GreaterThan(currLow) && !currLow.IsZero() { + lowestPrice = currLow + lowestTime = currTime + } + if highestPrice.LessThan(currHigh) { + if lowestTime.Equal(highestTime) { + // create distinction if the greatest drawdown occurs within the same candle + lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) + } + intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[i].GetInterval(), 0) + if err != nil { + log.Error(common.SubLoggers[common.CurrencyStatistics], err) + continue + } + if highestPrice.IsPositive() && lowestPrice.IsPositive() { + swings = append(swings, Swing{ + Highest: ValueAtTime{ + Time: highestTime, + Value: highestPrice, + }, + Lowest: ValueAtTime{ + Time: lowestTime, + Value: lowestPrice, + }, + DrawdownPercent: lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)), + IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), + }) + } + // reset the drawdown + highestPrice = currHigh + highestTime = currTime + lowestPrice = currLow + lowestTime = currTime + } + } + if (len(swings) > 0 && swings[len(swings)-1].Lowest.Value != closePrices[len(closePrices)-1].GetLowPrice()) || swings == nil { + // need to close out the final drawdown + if lowestTime.Equal(highestTime) { + // create distinction if the greatest drawdown occurs within the same candle + lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) + } + intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[0].GetInterval(), 0) + if err != nil { + return Swing{}, err + } + drawdownPercent := decimal.Zero + if highestPrice.GreaterThan(decimal.Zero) { + drawdownPercent = lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)) + } + if lowestTime.Equal(highestTime) { + // create distinction if the greatest drawdown occurs within the same candle + lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) + } + swings = append(swings, Swing{ + Highest: ValueAtTime{ + Time: highestTime, + Value: highestPrice, + }, + Lowest: ValueAtTime{ + Time: lowestTime, + Value: lowestPrice, + }, + DrawdownPercent: drawdownPercent, + IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), + }) + } + + var maxDrawdown Swing + if len(swings) > 0 { + maxDrawdown = swings[0] + } + for i := range swings { + if swings[i].DrawdownPercent.LessThan(maxDrawdown.DrawdownPercent) { + maxDrawdown = swings[i] + } + } + + return maxDrawdown, nil +} + +// CalculateBiggestValueAtTimeDrawdown calculates the biggest drawdown using a slice of ValueAtTimes +func CalculateBiggestValueAtTimeDrawdown(closePrices []ValueAtTime, interval gctkline.Interval) (Swing, error) { + if len(closePrices) == 0 { + return Swing{}, fmt.Errorf("%w to calculate drawdowns", errReceivedNoData) + } + var swings []Swing + lowestPrice := closePrices[0].Value + highestPrice := closePrices[0].Value + lowestTime := closePrices[0].Time + highestTime := closePrices[0].Time + + for i := range closePrices { + currHigh := closePrices[i].Value + currLow := closePrices[i].Value + currTime := closePrices[i].Time + if lowestPrice.GreaterThan(currLow) && !currLow.IsZero() { + lowestPrice = currLow + lowestTime = currTime + } + if highestPrice.LessThan(currHigh) && highestPrice.IsPositive() { + if lowestTime.Equal(highestTime) { + // create distinction if the greatest drawdown occurs within the same candle + lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) + } + intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0) + if err != nil { + return Swing{}, err + } + swings = append(swings, Swing{ + Highest: ValueAtTime{ + Time: highestTime, + Value: highestPrice, + }, + Lowest: ValueAtTime{ + Time: lowestTime, + Value: lowestPrice, + }, + DrawdownPercent: lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)), + IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), + }) + // reset the drawdown + highestPrice = currHigh + highestTime = currTime + lowestPrice = currLow + lowestTime = currTime + } + } + if (len(swings) > 0 && !swings[len(swings)-1].Lowest.Value.Equal(closePrices[len(closePrices)-1].Value)) || swings == nil { + // need to close out the final drawdown + if lowestTime.Equal(highestTime) { + // create distinction if the greatest drawdown occurs within the same candle + lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) + } + intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0) + if err != nil { + log.Error(common.SubLoggers[common.CurrencyStatistics], err) + } + drawdownPercent := decimal.Zero + if highestPrice.GreaterThan(decimal.Zero) { + drawdownPercent = lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)) + } + if lowestTime.Equal(highestTime) { + // create distinction if the greatest drawdown occurs within the same candle + lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) + } + swings = append(swings, Swing{ + Highest: ValueAtTime{ + Time: highestTime, + Value: highestPrice, + }, + Lowest: ValueAtTime{ + Time: lowestTime, + Value: lowestPrice, + }, + DrawdownPercent: drawdownPercent, + IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), + }) + } + + var maxDrawdown Swing + if len(swings) > 0 { + maxDrawdown = swings[0] + } + for i := range swings { + if swings[i].DrawdownPercent.LessThan(maxDrawdown.DrawdownPercent) { + maxDrawdown = swings[i] + } + } + + return maxDrawdown, nil +} + +// CalculateRatios creates arithmetic and geometric ratios from funding or currency pair data +func CalculateRatios(benchmarkRates, returnsPerCandle []decimal.Decimal, riskFreeRatePerCandle decimal.Decimal, maxDrawdown *Swing, logMessage string) (arithmeticStats, geometricStats *Ratios, err error) { + var arithmeticBenchmarkAverage, geometricBenchmarkAverage decimal.Decimal + arithmeticBenchmarkAverage, err = gctmath.DecimalArithmeticMean(benchmarkRates) + if err != nil { + return nil, nil, err + } + geometricBenchmarkAverage, err = gctmath.DecimalFinancialGeometricMean(benchmarkRates) + if err != nil { + return nil, nil, err + } + + riskFreeRateForPeriod := riskFreeRatePerCandle.Mul(decimal.NewFromInt(int64(len(benchmarkRates)))) + + var arithmeticReturnsPerCandle, geometricReturnsPerCandle, arithmeticSharpe, arithmeticSortino, + arithmeticInformation, arithmeticCalmar, geomSharpe, geomSortino, geomInformation, geomCalmar decimal.Decimal + + arithmeticReturnsPerCandle, err = gctmath.DecimalArithmeticMean(returnsPerCandle) + if err != nil { + return nil, nil, err + } + geometricReturnsPerCandle, err = gctmath.DecimalFinancialGeometricMean(returnsPerCandle) + if err != nil { + return nil, nil, err + } + + arithmeticSharpe, err = gctmath.DecimalSharpeRatio(returnsPerCandle, riskFreeRatePerCandle, arithmeticReturnsPerCandle) + if err != nil { + return nil, nil, err + } + arithmeticSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, arithmeticReturnsPerCandle) + if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { + if errors.Is(err, gctmath.ErrInexactConversion) { + log.Warnf(common.SubLoggers[common.Statistics], "%s funding arithmetic sortino ratio %v", logMessage, err) + } else { + return nil, nil, err + } + } + arithmeticInformation, err = gctmath.DecimalInformationRatio(returnsPerCandle, benchmarkRates, arithmeticReturnsPerCandle, arithmeticBenchmarkAverage) + if err != nil { + return nil, nil, err + } + arithmeticCalmar, err = gctmath.DecimalCalmarRatio(maxDrawdown.Highest.Value, maxDrawdown.Lowest.Value, arithmeticReturnsPerCandle, riskFreeRateForPeriod) + if err != nil { + return nil, nil, err + } + + arithmeticStats = &Ratios{} + if !arithmeticSharpe.IsZero() { + arithmeticStats.SharpeRatio = arithmeticSharpe + } + if !arithmeticSortino.IsZero() { + arithmeticStats.SortinoRatio = arithmeticSortino + } + if !arithmeticInformation.IsZero() { + arithmeticStats.InformationRatio = arithmeticInformation + } + if !arithmeticCalmar.IsZero() { + arithmeticStats.CalmarRatio = arithmeticCalmar + } + + geomSharpe, err = gctmath.DecimalSharpeRatio(returnsPerCandle, riskFreeRatePerCandle, geometricReturnsPerCandle) + if err != nil { + return nil, nil, err + } + geomSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, geometricReturnsPerCandle) + if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { + if errors.Is(err, gctmath.ErrInexactConversion) { + log.Warnf(common.SubLoggers[common.Statistics], "%s geometric sortino ratio %v", logMessage, err) + } else { + return nil, nil, err + } + } + geomInformation, err = gctmath.DecimalInformationRatio(returnsPerCandle, benchmarkRates, geometricReturnsPerCandle, geometricBenchmarkAverage) + if err != nil { + return nil, nil, err + } + geomCalmar, err = gctmath.DecimalCalmarRatio(maxDrawdown.Highest.Value, maxDrawdown.Lowest.Value, geometricReturnsPerCandle, riskFreeRateForPeriod) + if err != nil { + return nil, nil, err + } + geometricStats = &Ratios{} + if !arithmeticSharpe.IsZero() { + geometricStats.SharpeRatio = geomSharpe + } + if !arithmeticSortino.IsZero() { + geometricStats.SortinoRatio = geomSortino + } + if !arithmeticInformation.IsZero() { + geometricStats.InformationRatio = geomInformation + } + if !arithmeticCalmar.IsZero() { + geometricStats.CalmarRatio = geomCalmar + } + + return arithmeticStats, geometricStats, nil +} diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 230a8f2ea2f..0fe2ba93945 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -2,19 +2,12 @@ package statistics import ( "fmt" - "sort" - "time" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" gctcommon "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/common/convert" gctmath "github.com/thrasher-corp/gocryptotrader/common/math" - "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/log" ) // CalculateResults calculates all statistics for the exchange, asset, currency pair @@ -28,10 +21,15 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e last := c.Events[len(c.Events)-1] lastPrice := last.DataEvent.GetClosePrice() for i := range last.Transactions.Orders { - if last.Transactions.Orders[i].Order.Side == gctorder.Buy { + switch last.Transactions.Orders[i].Order.Side { + case gctorder.Buy: c.BuyOrders++ - } else if last.Transactions.Orders[i].Order.Side == gctorder.Sell { + case gctorder.Sell: c.SellOrders++ + case gctorder.Long: + c.LongOrders++ + case gctorder.Short: + c.ShortOrders++ } } for i := range c.Events { @@ -118,8 +116,7 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e c.TotalAssetValue = last.Holdings.BaseValue.Round(8) if last.PNL != nil { c.UnrealisedPNL = last.PNL.Result.UnrealisedPNL - // ???? - c.RealisedPNL = last.PNL.Result.RealisedPNLBeforeFees + c.RealisedPNL = last.PNL.Result.RealisedPNL } if len(errs) > 0 { return errs @@ -127,195 +124,6 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e return nil } -// PrintResults outputs all calculated statistics to the command line -func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency.Pair, usingExchangeLevelFunding bool) { - var errs gctcommon.Errors - sort.Slice(c.Events, func(i, j int) bool { - return c.Events[i].DataEvent.GetTime().Before(c.Events[j].DataEvent.GetTime()) - }) - last := c.Events[len(c.Events)-1] - first := c.Events[0] - c.StartingClosePrice = first.DataEvent.GetClosePrice() - c.EndingClosePrice = last.DataEvent.GetClosePrice() - c.TotalOrders = c.BuyOrders + c.SellOrders - last.Holdings.TotalValueLost = last.Holdings.TotalValueLostToSlippage.Add(last.Holdings.TotalValueLostToVolumeSizing) - sep := fmt.Sprintf("%v %v %v |\t", e, a, p) - currStr := fmt.Sprintf("------------------Stats for %v %v %v------------------------------------------", e, a, p) - log.Infof(common.SubLoggers[common.CurrencyStatistics], currStr[:61]) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total orders: %s\n\n", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) - - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Max Drawdown-------------------------------") - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Drawdown length: %s\n\n", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ",")) - if !usingExchangeLevelFunding { - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Ratios------------------------------------------------") - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Rates-------------------------------------------------") - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ",")) - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Arithmetic--------------------------------------------") - if c.ShowMissingDataWarning { - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") - } - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.ArithmeticRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.ArithmeticRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4)) - - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Geometric--------------------------------------------") - if c.ShowMissingDataWarning { - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") - } - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.GeometricRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.GeometricRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.GeometricRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v\n\n", sep, c.GeometricRatios.CalmarRatio.Round(4)) - } - - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Results------------------------------------") - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Starting Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Finishing Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice, 8, ".", ",")) - - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) - if !usingExchangeLevelFunding { - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Strategy movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.StrategyMovement, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Did it beat the market: %v", sep, c.StrategyMovement.GreaterThan(c.MarketMovement)) - } - - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to volume sizing: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToVolumeSizing, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to slippage: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToSlippage, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Value lost: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLost, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Fees: %s\n\n", sep, convert.DecimalToHumanFriendlyString(c.TotalFees, 8, ".", ",")) - - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings value: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalAssetValue, 8, ".", ",")) - if !usingExchangeLevelFunding { - // the following have no direct translation to individual exchange level funds as they - // combine base and quote values - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.QuoteSize, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final total value: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) - } - - if last.PNL != nil { - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final unPNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final PNL: %s\n\n", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNL, 8, ".", ",")) - } - if len(errs) > 0 { - log.Info(common.SubLoggers[common.CurrencyStatistics], "------------------Errors-------------------------------------") - for i := range errs { - log.Error(common.SubLoggers[common.CurrencyStatistics], errs[i].Error()) - } - } -} - -// CalculateBiggestEventDrawdown calculates the biggest drawdown using a slice of DataEvents -func CalculateBiggestEventDrawdown(closePrices []common.DataEventHandler) (Swing, error) { - if len(closePrices) == 0 { - return Swing{}, fmt.Errorf("%w to calculate drawdowns", errReceivedNoData) - } - var swings []Swing - lowestPrice := closePrices[0].GetLowPrice() - highestPrice := closePrices[0].GetHighPrice() - lowestTime := closePrices[0].GetTime() - highestTime := closePrices[0].GetTime() - interval := closePrices[0].GetInterval() - - for i := range closePrices { - currHigh := closePrices[i].GetHighPrice() - currLow := closePrices[i].GetLowPrice() - currTime := closePrices[i].GetTime() - if lowestPrice.GreaterThan(currLow) && !currLow.IsZero() { - lowestPrice = currLow - lowestTime = currTime - } - if highestPrice.LessThan(currHigh) { - if lowestTime.Equal(highestTime) { - // create distinction if the greatest drawdown occurs within the same candle - lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) - } - intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[i].GetInterval(), 0) - if err != nil { - log.Error(common.SubLoggers[common.CurrencyStatistics], err) - continue - } - if highestPrice.IsPositive() && lowestPrice.IsPositive() { - swings = append(swings, Swing{ - Highest: ValueAtTime{ - Time: highestTime, - Value: highestPrice, - }, - Lowest: ValueAtTime{ - Time: lowestTime, - Value: lowestPrice, - }, - DrawdownPercent: lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)), - IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), - }) - } - // reset the drawdown - highestPrice = currHigh - highestTime = currTime - lowestPrice = currLow - lowestTime = currTime - } - } - if (len(swings) > 0 && swings[len(swings)-1].Lowest.Value != closePrices[len(closePrices)-1].GetLowPrice()) || swings == nil { - // need to close out the final drawdown - if lowestTime.Equal(highestTime) { - // create distinction if the greatest drawdown occurs within the same candle - lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) - } - intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[0].GetInterval(), 0) - if err != nil { - return Swing{}, err - } - drawdownPercent := decimal.Zero - if highestPrice.GreaterThan(decimal.Zero) { - drawdownPercent = lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)) - } - if lowestTime.Equal(highestTime) { - // create distinction if the greatest drawdown occurs within the same candle - lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) - } - swings = append(swings, Swing{ - Highest: ValueAtTime{ - Time: highestTime, - Value: highestPrice, - }, - Lowest: ValueAtTime{ - Time: lowestTime, - Value: lowestPrice, - }, - DrawdownPercent: drawdownPercent, - IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), - }) - } - - var maxDrawdown Swing - if len(swings) > 0 { - maxDrawdown = swings[0] - } - for i := range swings { - if swings[i].DrawdownPercent.LessThan(maxDrawdown.DrawdownPercent) { - maxDrawdown = swings[i] - } - } - - return maxDrawdown, nil -} - func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() { for i := range c.Events { if c.Events[i].Holdings.BaseSize.Mul(c.Events[i].DataEvent.GetClosePrice()).GreaterThan(c.HighestCommittedFunds.Value) { @@ -324,95 +132,3 @@ func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() { } } } - -// CalculateBiggestValueAtTimeDrawdown calculates the biggest drawdown using a slice of ValueAtTimes -func CalculateBiggestValueAtTimeDrawdown(closePrices []ValueAtTime, interval gctkline.Interval) (Swing, error) { - if len(closePrices) == 0 { - return Swing{}, fmt.Errorf("%w to calculate drawdowns", errReceivedNoData) - } - var swings []Swing - lowestPrice := closePrices[0].Value - highestPrice := closePrices[0].Value - lowestTime := closePrices[0].Time - highestTime := closePrices[0].Time - - for i := range closePrices { - currHigh := closePrices[i].Value - currLow := closePrices[i].Value - currTime := closePrices[i].Time - if lowestPrice.GreaterThan(currLow) && !currLow.IsZero() { - lowestPrice = currLow - lowestTime = currTime - } - if highestPrice.LessThan(currHigh) && highestPrice.IsPositive() { - if lowestTime.Equal(highestTime) { - // create distinction if the greatest drawdown occurs within the same candle - lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) - } - intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0) - if err != nil { - return Swing{}, err - } - swings = append(swings, Swing{ - Highest: ValueAtTime{ - Time: highestTime, - Value: highestPrice, - }, - Lowest: ValueAtTime{ - Time: lowestTime, - Value: lowestPrice, - }, - DrawdownPercent: lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)), - IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), - }) - // reset the drawdown - highestPrice = currHigh - highestTime = currTime - lowestPrice = currLow - lowestTime = currTime - } - } - if (len(swings) > 0 && !swings[len(swings)-1].Lowest.Value.Equal(closePrices[len(closePrices)-1].Value)) || swings == nil { - // need to close out the final drawdown - if lowestTime.Equal(highestTime) { - // create distinction if the greatest drawdown occurs within the same candle - lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) - } - intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0) - if err != nil { - log.Error(common.SubLoggers[common.CurrencyStatistics], err) - } - drawdownPercent := decimal.Zero - if highestPrice.GreaterThan(decimal.Zero) { - drawdownPercent = lowestPrice.Sub(highestPrice).Div(highestPrice).Mul(decimal.NewFromInt(100)) - } - if lowestTime.Equal(highestTime) { - // create distinction if the greatest drawdown occurs within the same candle - lowestTime = lowestTime.Add(interval.Duration() - time.Nanosecond) - } - swings = append(swings, Swing{ - Highest: ValueAtTime{ - Time: highestTime, - Value: highestPrice, - }, - Lowest: ValueAtTime{ - Time: lowestTime, - Value: lowestPrice, - }, - DrawdownPercent: drawdownPercent, - IntervalDuration: int64(len(intervals.Ranges[0].Intervals)), - }) - } - - var maxDrawdown Swing - if len(swings) > 0 { - maxDrawdown = swings[0] - } - for i := range swings { - if swings[i].DrawdownPercent.LessThan(maxDrawdown.DrawdownPercent) { - maxDrawdown = swings[i] - } - } - - return maxDrawdown, nil -} diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index 862118a7f79..1fc832f2454 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -7,12 +7,10 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/funding" - "github.com/thrasher-corp/gocryptotrader/common/convert" gctmath "github.com/thrasher-corp/gocryptotrader/common/math" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" - "github.com/thrasher-corp/gocryptotrader/log" ) // CalculateFundingStatistics calculates funding statistics for total USD strategy results @@ -169,28 +167,47 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f Value: closePrices[len(closePrices)-1].USDClosePrice, } for i := range closePrices { - if closePrices[i].USDClosePrice.LessThan(item.LowestClosePrice.Value) || item.LowestClosePrice.Value.IsZero() { + if closePrices[i].USDClosePrice.LessThan(item.LowestClosePrice.Value) || !item.LowestClosePrice.Set { item.LowestClosePrice.Value = closePrices[i].USDClosePrice item.LowestClosePrice.Time = closePrices[i].Time + item.LowestClosePrice.Set = true } - if closePrices[i].USDClosePrice.GreaterThan(item.HighestClosePrice.Value) || item.HighestClosePrice.Value.IsZero() { + if closePrices[i].USDClosePrice.GreaterThan(item.HighestClosePrice.Value) || !item.HighestClosePrice.Set { item.HighestClosePrice.Value = closePrices[i].USDClosePrice item.HighestClosePrice.Time = closePrices[i].Time + item.HighestClosePrice.Set = true } } item.IsCollateral = reportItem.IsCollateral - if item.IsCollateral { - //item.LowestCollateral = something() - //item.HighestCollateral = something() - //item.EndingCollateral = something() - //item.InitialCollateral = something() - } else if reportItem.Asset.IsFutures() { - // item.HighestRPNL = somethingElse() - // item.LowestRPNL = somethingElse() - // item.FinalUPNL = somethingElse() - // item.FinalRPNL = somethingElse() - // item.HighestUPNL = somethingElse() - // item.LowestUPNL = somethingElse() + if reportItem.Asset.IsFutures() { + var lowest, highest, initial, final ValueAtTime + initial.Value = reportItem.Snapshots[0].Available + initial.Time = reportItem.Snapshots[0].Time + final.Value = reportItem.Snapshots[len(reportItem.Snapshots)-1].Available + final.Time = reportItem.Snapshots[len(reportItem.Snapshots)-1].Time + for i := range reportItem.Snapshots { + if reportItem.Snapshots[i].Available.LessThan(lowest.Value) || !lowest.Set { + lowest.Value = reportItem.Snapshots[i].Available + lowest.Time = reportItem.Snapshots[i].Time + lowest.Set = true + } + if reportItem.Snapshots[i].Available.GreaterThan(highest.Value) || !lowest.Set { + highest.Value = reportItem.Snapshots[i].Available + highest.Time = reportItem.Snapshots[i].Time + highest.Set = true + } + } + if reportItem.IsCollateral { + item.LowestCollateral = lowest + item.HighestCollateral = highest + item.InitialCollateral = initial + item.FinalCollateral = final + } else { + item.LowestHoldings = lowest + item.HighestHoldings = highest + item.InitialHoldings = initial + item.FinalHoldings = final + } } if !reportItem.IsCollateral { for i := range relatedStats { @@ -247,116 +264,3 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f item.MaxDrawdown, err = CalculateBiggestEventDrawdown(s) return item, err } - -// PrintResults outputs all calculated funding statistics to the command line -func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { - if f.Report == nil { - return fmt.Errorf("%w requires report to be generated", common.ErrNilArguments) - } - var spotResults, futuresResults []FundingItemStatistics - for i := range f.Items { - if f.Items[i].ReportItem.Asset.IsFutures() { - futuresResults = append(futuresResults, f.Items[i]) - } else { - spotResults = append(spotResults, f.Items[i]) - } - } - if len(spotResults) > 0 || len(futuresResults) > 0 { - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding------------------------------------") - } - if len(spotResults) > 0 { - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding Spot Item Results------------------") - for i := range spotResults { - sep := fmt.Sprintf("%v %v %v |\t", spotResults[i].ReportItem.Exchange, spotResults[i].ReportItem.Asset, spotResults[i].ReportItem.Currency) - if !spotResults[i].ReportItem.PairedWith.IsEmpty() { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Paired with: %v", sep, spotResults[i].ReportItem.PairedWith) - } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.InitialFunds, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.FinalFunds, 8, ".", ",")) - - if !f.Report.DisableUSDTracking && f.Report.UsingExchangeLevelFunding { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDInitialFunds, 2, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDFinalFunds, 2, ".", ",")) - } - if spotResults[i].ReportItem.ShowInfinite { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: ∞%%", sep) - } else { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.Difference, 8, ".", ",")) - } - if spotResults[i].ReportItem.TransferFee.GreaterThan(decimal.Zero) { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.TransferFee, 8, ".", ",")) - } - log.Info(common.SubLoggers[common.FundingStatistics], "") - } - } - if len(futuresResults) > 0 { - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Funding Futures Item Results---------------") - for i := range futuresResults { - sep := fmt.Sprintf("%v %v %v |\t", futuresResults[i].ReportItem.Exchange, futuresResults[i].ReportItem.Asset, futuresResults[i].ReportItem.Currency) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Is Collateral: %v", sep, futuresResults[i].IsCollateral) - if futuresResults[i].IsCollateral { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial Collateral: %v %v at %v", sep, futuresResults[i].InitialCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialCollateral.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Collateral: %v %v at %v", sep, futuresResults[i].EndingCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].EndingCollateral.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].LowestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestCollateral.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].HighestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestCollateral.Time) - } else { - if !futuresResults[i].ReportItem.PairedWith.IsEmpty() { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Collateral currency: %v", sep, futuresResults[i].ReportItem.PairedWith) - } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Unrealised PNL: %v %v at %v", sep, futuresResults[i].LowestUPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].LowestUPNL.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Unrealised PNL: %v %v at %v", sep, futuresResults[i].HighestUPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].HighestUPNL.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Realised PNL: %v %v at %v", sep, futuresResults[i].LowestRPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].LowestRPNL.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Realised PNL: %v %v at %v", sep, futuresResults[i].HighestRPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].HighestRPNL.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Unrealised PNL: %v %v at %v", sep, futuresResults[i].FinalUPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].FinalUPNL.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Realised PNL: %v %v at %v", sep, futuresResults[i].FinalRPNL.Value, futuresResults[i].ReportItem.PairedWith, futuresResults[i].FinalRPNL.Time) - } - log.Info(common.SubLoggers[common.FundingStatistics], "") - } - } - if f.Report.DisableUSDTracking { - return nil - } - log.Info(common.SubLoggers[common.FundingStatistics], "------------------USD Tracking Totals------------------------") - sep := "USD Tracking Total |\t" - - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) - - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Ratios------------------------------------------------") - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Rates-------------------------------------------------") - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ",")) - if f.TotalUSDStatistics.ArithmeticRatios == nil || f.TotalUSDStatistics.GeometricRatios == nil { - return fmt.Errorf("%w missing ratio calculations", common.ErrNilArguments) - } - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Arithmetic--------------------------------------------") - if wasAnyDataMissing { - log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") - } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4)) - - log.Info(common.SubLoggers[common.FundingStatistics], "------------------Geometric--------------------------------------------") - if wasAnyDataMissing { - log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") - } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v\n\n", sep, f.TotalUSDStatistics.GeometricRatios.CalmarRatio.Round(4)) - - return nil -} diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go new file mode 100644 index 00000000000..9d38225b807 --- /dev/null +++ b/backtester/eventhandlers/statistics/printresults.go @@ -0,0 +1,375 @@ +package statistics + +import ( + "fmt" + "sort" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/log" +) + +const ( + limit12 = 12 + limit14 = 14 + limit10 = 10 +) + +// addReason basic helper to append event reason if one is there +func addReason(reason, msg string) string { + if reason != "" { + msg = msg + "\tReason: " + reason + } + return msg +} + +// PrintTotalResults outputs all results to the CMD +func (s *Statistic) PrintTotalResults() { + log.Info(common.SubLoggers[common.Statistics], common.ColourH1+"------------------Strategy-----------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.Statistics], "Strategy Name: %v", s.StrategyName) + log.Infof(common.SubLoggers[common.Statistics], "Strategy Nickname: %v", s.StrategyNickname) + log.Infof(common.SubLoggers[common.Statistics], "Strategy Goal: %v\n\n", s.StrategyGoal) + + log.Info(common.SubLoggers[common.Statistics], common.ColourH2+"------------------Total Results------------------------------"+common.ColourDefault) + log.Info(common.SubLoggers[common.Statistics], common.ColourH3+"------------------Orders-------------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.Statistics], "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) + log.Infof(common.SubLoggers[common.Statistics], "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) + log.Infof(common.SubLoggers[common.Statistics], "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) + + if s.BiggestDrawdown != nil { + log.Info(common.SubLoggers[common.Statistics], common.ColourH3+"------------------Biggest Drawdown-----------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.Statistics], "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair) + log.Infof(common.SubLoggers[common.Statistics], "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time) + log.Infof(common.SubLoggers[common.Statistics], "Lowest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Lowest.Value, 8, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Lowest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Lowest.Time) + log.Infof(common.SubLoggers[common.Statistics], "Calculated Drawdown: %s%%", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.DrawdownPercent, 2, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Difference: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value.Sub(s.BiggestDrawdown.MaxDrawdown.Lowest.Value), 8, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Drawdown length: %v\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ",")) + } + if s.BestMarketMovement != nil && s.BestStrategyResults != nil { + log.Info(common.SubLoggers[common.Statistics], common.ColourH4+"------------------Orders----------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.Statistics], "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ",")) + log.Infof(common.SubLoggers[common.Statistics], "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ",")) + } +} + +// PrintAllEventsChronologically outputs all event details in the CMD +// rather than separated by exchange, asset and currency pair, it's +// grouped by time to allow a clearer picture of events +func (s *Statistic) PrintAllEventsChronologically() { + var results []eventOutputHolder + log.Info(common.SubLoggers[common.Statistics], common.ColourH1+"------------------Events-------------------------------------"+common.ColourDefault) + var errs gctcommon.Errors + for exch, x := range s.ExchangeAssetPairStatistics { + for a, y := range x { + for pair, currencyStatistic := range y { + for i := range currencyStatistic.Events { + switch { + case currencyStatistic.Events[i].FillEvent != nil: + direction := currencyStatistic.Events[i].FillEvent.GetDirection() + if direction == common.CouldNotBuy || + direction == common.CouldNotSell || + direction == common.MissingData || + direction == common.DoNothing || + direction == common.TransferredFunds || + direction == "" { + colour := common.ColourProblem + if direction == common.DoNothing { + colour = common.ColourDarkGrey + } + msg := fmt.Sprintf(colour+ + "%v %v%v%v| Price: $%v\tDirection: %v", + currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), + fSIL(exch, limit12), + fSIL(a.String(), limit10), + fSIL(currencyStatistic.Events[i].FillEvent.Pair().String(), limit14), + currencyStatistic.Events[i].FillEvent.GetClosePrice().Round(8), + currencyStatistic.Events[i].FillEvent.GetDirection()) + msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) + msg = msg + common.ColourDefault + results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) + } else { + msg := fmt.Sprintf(common.ColourSuccess+ + "%v %v%v%v| Price: $%v\tAmount: %v\tFee: $%v\tTotal: $%v\tDirection %v", + currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), + fSIL(exch, limit12), + fSIL(a.String(), limit10), + fSIL(currencyStatistic.Events[i].FillEvent.Pair().String(), limit14), + currencyStatistic.Events[i].FillEvent.GetPurchasePrice().Round(8), + currencyStatistic.Events[i].FillEvent.GetAmount().Round(8), + currencyStatistic.Events[i].FillEvent.GetExchangeFee().Round(8), + currencyStatistic.Events[i].FillEvent.GetTotal().Round(8), + currencyStatistic.Events[i].FillEvent.GetDirection()) + msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) + msg = msg + common.ColourDefault + results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) + } + case currencyStatistic.Events[i].SignalEvent != nil: + msg := fmt.Sprintf("%v %v%v%v| Price: $%v", + currencyStatistic.Events[i].SignalEvent.GetTime().Format(gctcommon.SimpleTimeFormat), + fSIL(exch, limit12), + fSIL(a.String(), limit10), + fSIL(currencyStatistic.Events[i].SignalEvent.Pair().String(), limit14), + currencyStatistic.Events[i].SignalEvent.GetPrice().Round(8)) + msg = addReason(currencyStatistic.Events[i].SignalEvent.GetReason(), msg) + msg = msg + common.ColourDefault + results = addEventOutputToTime(results, currencyStatistic.Events[i].SignalEvent.GetTime(), msg) + case currencyStatistic.Events[i].DataEvent != nil: + msg := fmt.Sprintf("%v %v%v%v| Price: $%v", + currencyStatistic.Events[i].DataEvent.GetTime().Format(gctcommon.SimpleTimeFormat), + fSIL(exch, limit12), + fSIL(a.String(), limit10), + fSIL(currencyStatistic.Events[i].DataEvent.Pair().String(), limit14), + currencyStatistic.Events[i].DataEvent.GetClosePrice().Round(8)) + msg = addReason(currencyStatistic.Events[i].DataEvent.GetReason(), msg) + msg = msg + common.ColourDefault + results = addEventOutputToTime(results, currencyStatistic.Events[i].DataEvent.GetTime(), msg) + default: + errs = append(errs, fmt.Errorf(common.ColourError+"%v%v%v unexpected data received %+v"+common.ColourDefault, exch, a, fSIL(pair.String(), limit14), currencyStatistic.Events[i])) + } + } + } + } + } + + sort.Slice(results, func(i, j int) bool { + b1 := results[i] + b2 := results[j] + return b1.Time.Before(b2.Time) + }) + for i := range results { + for j := range results[i].Events { + log.Info(common.SubLoggers[common.Statistics], results[i].Events[j]) + } + } + if len(errs) > 0 { + log.Info(common.SubLoggers[common.Statistics], common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) + for i := range errs { + log.Error(common.SubLoggers[common.Statistics], errs[i].Error()) + } + } +} + +// PrintResults outputs all calculated statistics to the command line +func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency.Pair, usingExchangeLevelFunding bool) { + var errs gctcommon.Errors + sort.Slice(c.Events, func(i, j int) bool { + return c.Events[i].DataEvent.GetTime().Before(c.Events[j].DataEvent.GetTime()) + }) + last := c.Events[len(c.Events)-1] + first := c.Events[0] + c.StartingClosePrice = first.DataEvent.GetClosePrice() + c.EndingClosePrice = last.DataEvent.GetClosePrice() + c.TotalOrders = c.BuyOrders + c.SellOrders + last.Holdings.TotalValueLost = last.Holdings.TotalValueLostToSlippage.Add(last.Holdings.TotalValueLostToVolumeSizing) + sep := fmt.Sprintf("%v %v %v |\t", fSIL(e, limit12), fSIL(a.String(), limit10), fSIL(p.String(), limit14)) + currStr := fmt.Sprintf(common.ColourH1+"------------------Stats for %v %v %v------------------------------------------------------"+common.ColourDefault, e, a, p) + log.Infof(common.SubLoggers[common.CurrencyStatistics], currStr[:70]) + if a.IsFutures() { + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Long orders: %s", sep, convert.IntToHumanFriendlyString(c.LongOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Short orders: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Unrealised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Unrealised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Realised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Realised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) + + } else { + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) + } + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) + + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Max Drawdown-------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Drawdown length: %s", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ",")) + if !usingExchangeLevelFunding { + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Ratios------------------------------------------------"+common.ColourDefault) + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH3+"------------------Rates-------------------------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ",")) + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH4+"------------------Arithmetic--------------------------------------------"+common.ColourDefault) + if c.ShowMissingDataWarning { + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") + } + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.ArithmeticRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.ArithmeticRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4)) + + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH4+"------------------Geometric--------------------------------------------"+common.ColourDefault) + if c.ShowMissingDataWarning { + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") + } + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.GeometricRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.GeometricRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.GeometricRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v", sep, c.GeometricRatios.CalmarRatio.Round(4)) + } + + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Results------------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Starting Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Finishing Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice, 8, ".", ",")) + + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) + if !usingExchangeLevelFunding { + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Strategy movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.StrategyMovement, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Did it beat the market: %v", sep, c.StrategyMovement.GreaterThan(c.MarketMovement)) + } + + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to volume sizing: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToVolumeSizing, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to slippage: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToSlippage, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Value lost: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLost, 2, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Fees: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalFees, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings value: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalAssetValue, 8, ".", ",")) + if !usingExchangeLevelFunding { + // the following have no direct translation to individual exchange level funds as they + // combine base and quote values + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.QuoteSize, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final total value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) + } + + if last.PNL != nil { + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Unrealised PNL: %s", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Realised PNL: %s", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNL, 8, ".", ",")) + } + if len(errs) > 0 { + log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) + for i := range errs { + log.Error(common.SubLoggers[common.CurrencyStatistics], errs[i].Error()) + } + } +} + +// PrintResults outputs all calculated funding statistics to the command line +func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { + if f.Report == nil { + return fmt.Errorf("%w requires report to be generated", common.ErrNilArguments) + } + var spotResults, futuresResults []FundingItemStatistics + for i := range f.Items { + if f.Items[i].ReportItem.Asset.IsFutures() { + futuresResults = append(futuresResults, f.Items[i]) + } else { + spotResults = append(spotResults, f.Items[i]) + } + } + if len(spotResults) > 0 || len(futuresResults) > 0 { + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH1+"------------------Funding------------------------------------"+common.ColourDefault) + } + if len(spotResults) > 0 { + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH2+"------------------Funding Spot Item Results------------------"+common.ColourDefault) + for i := range spotResults { + sep := fmt.Sprintf("%v%v%v| ", fSIL(spotResults[i].ReportItem.Exchange, limit12), fSIL(spotResults[i].ReportItem.Asset.String(), limit10), fSIL(spotResults[i].ReportItem.Currency.String(), limit14)) + if !spotResults[i].ReportItem.PairedWith.IsEmpty() { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Paired with: %v", sep, spotResults[i].ReportItem.PairedWith) + } + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.InitialFunds, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.FinalFunds, 8, ".", ",")) + + if !f.Report.DisableUSDTracking && f.Report.UsingExchangeLevelFunding { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDInitialFunds, 2, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDFinalFunds, 2, ".", ",")) + } + if spotResults[i].ReportItem.ShowInfinite { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: ∞%%", sep) + } else { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.Difference, 8, ".", ",")) + } + if spotResults[i].ReportItem.TransferFee.GreaterThan(decimal.Zero) { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.TransferFee, 8, ".", ",")) + } + if i != len(spotResults)-1 { + log.Info(common.SubLoggers[common.FundingStatistics], "") + } + } + } + if len(futuresResults) > 0 { + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH2+"------------------Funding Futures Item Results---------------"+common.ColourDefault) + for i := range futuresResults { + sep := fmt.Sprintf("%v%v%v| ", fSIL(futuresResults[i].ReportItem.Exchange, limit12), fSIL(futuresResults[i].ReportItem.Asset.String(), limit10), fSIL(futuresResults[i].ReportItem.Currency.String(), limit14)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Is Collateral: %v", sep, futuresResults[i].IsCollateral) + if futuresResults[i].IsCollateral { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial Collateral: %v %v at %v", sep, futuresResults[i].InitialCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialCollateral.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Collateral: %v %v at %v", sep, futuresResults[i].FinalCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalCollateral.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].LowestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestCollateral.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Collateral: %v %v at %v", sep, futuresResults[i].HighestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestCollateral.Time) + } else { + if !futuresResults[i].ReportItem.PairedWith.IsEmpty() { + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Collateral currency: %v", sep, futuresResults[i].ReportItem.PairedWith) + } + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Contract Holdings: %v %v at %v", sep, futuresResults[i].LowestHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestHoldings.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Contract Holdings: %v %v at %v", sep, futuresResults[i].HighestHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestHoldings.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial Contract Holdings: %v %v at %v", sep, futuresResults[i].InitialHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialHoldings.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Contract Holdings: %v %v at %v", sep, futuresResults[i].FinalHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalHoldings.Time) + } + if i != len(futuresResults)-1 { + log.Info(common.SubLoggers[common.FundingStatistics], "") + } + } + } + if f.Report.DisableUSDTracking { + return nil + } + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH2+"------------------USD Tracking Totals------------------------"+common.ColourDefault) + sep := "USD Tracking Total |\t" + + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Long Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.LongOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Short Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.ShortOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) + + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH3+"------------------Ratios------------------------------------------------"+common.ColourDefault) + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH4+"------------------Rates-------------------------------------------------"+common.ColourDefault) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ",")) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ",")) + if f.TotalUSDStatistics.ArithmeticRatios == nil || f.TotalUSDStatistics.GeometricRatios == nil { + return fmt.Errorf("%w missing ratio calculations", common.ErrNilArguments) + } + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH4+"------------------Arithmetic--------------------------------------------"+common.ColourDefault) + if wasAnyDataMissing { + log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") + } + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4)) + + log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH4+"------------------Geometric--------------------------------------------"+common.ColourDefault) + if wasAnyDataMissing { + log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") + log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") + } + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SharpeRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SortinoRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.InformationRatio.Round(4)) + log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v\n\n", sep, f.TotalUSDStatistics.GeometricRatios.CalmarRatio.Round(4)) + + return nil +} diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 1fb2859836f..42dc27555ac 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -2,12 +2,9 @@ package statistics import ( "encoding/json" - "errors" "fmt" - "sort" "time" - "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" @@ -15,9 +12,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" - gctcommon "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/common/convert" - gctmath "github.com/thrasher-corp/gocryptotrader/common/math" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/log" @@ -235,37 +229,6 @@ func (s *Statistic) CalculateAllResults() error { return nil } -// PrintTotalResults outputs all results to the CMD -func (s *Statistic) PrintTotalResults() { - log.Info(common.SubLoggers[common.Statistics], "------------------Strategy-----------------------------------") - log.Infof(common.SubLoggers[common.Statistics], "Strategy Name: %v", s.StrategyName) - log.Infof(common.SubLoggers[common.Statistics], "Strategy Nickname: %v", s.StrategyNickname) - log.Infof(common.SubLoggers[common.Statistics], "Strategy Goal: %v\n\n", s.StrategyGoal) - - log.Info(common.SubLoggers[common.Statistics], "------------------Total Results------------------------------") - log.Info(common.SubLoggers[common.Statistics], "------------------Orders-------------------------------------") - log.Infof(common.SubLoggers[common.Statistics], "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) - log.Infof(common.SubLoggers[common.Statistics], "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) - log.Infof(common.SubLoggers[common.Statistics], "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) - - if s.BiggestDrawdown != nil { - log.Info(common.SubLoggers[common.Statistics], "------------------Biggest Drawdown-----------------------") - log.Infof(common.SubLoggers[common.Statistics], "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair) - log.Infof(common.SubLoggers[common.Statistics], "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time) - log.Infof(common.SubLoggers[common.Statistics], "Lowest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Lowest.Value, 8, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Lowest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Lowest.Time) - log.Infof(common.SubLoggers[common.Statistics], "Calculated Drawdown: %s%%", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.DrawdownPercent, 2, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Difference: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value.Sub(s.BiggestDrawdown.MaxDrawdown.Lowest.Value), 8, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Drawdown length: %v\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ",")) - } - if s.BestMarketMovement != nil && s.BestStrategyResults != nil { - log.Info(common.SubLoggers[common.Statistics], "------------------Orders----------------------------------") - log.Infof(common.SubLoggers[common.Statistics], "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ",")) - } -} - // GetBestMarketPerformer returns the best final market movement func (s *Statistic) GetBestMarketPerformer(results []FinalResultsHolder) *FinalResultsHolder { result := &FinalResultsHolder{} @@ -314,94 +277,6 @@ func addEventOutputToTime(events []eventOutputHolder, t time.Time, message strin return events } -// PrintAllEventsChronologically outputs all event details in the CMD -// rather than separated by exchange, asset and currency pair, it's -// grouped by time to allow a clearer picture of events -func (s *Statistic) PrintAllEventsChronologically() { - var results []eventOutputHolder - log.Info(common.SubLoggers[common.Statistics], "------------------Events-------------------------------------") - var errs gctcommon.Errors - for exch, x := range s.ExchangeAssetPairStatistics { - for a, y := range x { - for pair, currencyStatistic := range y { - for i := range currencyStatistic.Events { - switch { - case currencyStatistic.Events[i].FillEvent != nil: - direction := currencyStatistic.Events[i].FillEvent.GetDirection() - if direction == common.CouldNotBuy || - direction == common.CouldNotSell || - direction == common.DoNothing || - direction == common.MissingData || - direction == common.TransferredFunds || - direction == "" { - results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), - fmt.Sprintf("%v %v %v %v | Price: $%v - Direction: %v - Reason: %s", - currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), - currencyStatistic.Events[i].FillEvent.GetExchange(), - currencyStatistic.Events[i].FillEvent.GetAssetType(), - currencyStatistic.Events[i].FillEvent.Pair(), - currencyStatistic.Events[i].FillEvent.GetClosePrice().Round(8), - currencyStatistic.Events[i].FillEvent.GetDirection(), - currencyStatistic.Events[i].FillEvent.GetReason())) - } else { - results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), - fmt.Sprintf("%v %v %v %v | Price: $%v - Amount: %v - Fee: $%v - Total: $%v - Direction %v - Reason: %s", - currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), - currencyStatistic.Events[i].FillEvent.GetExchange(), - currencyStatistic.Events[i].FillEvent.GetAssetType(), - currencyStatistic.Events[i].FillEvent.Pair(), - currencyStatistic.Events[i].FillEvent.GetPurchasePrice().Round(8), - currencyStatistic.Events[i].FillEvent.GetAmount().Round(8), - currencyStatistic.Events[i].FillEvent.GetExchangeFee().Round(8), - currencyStatistic.Events[i].FillEvent.GetTotal().Round(8), - currencyStatistic.Events[i].FillEvent.GetDirection(), - currencyStatistic.Events[i].FillEvent.GetReason(), - )) - } - case currencyStatistic.Events[i].SignalEvent != nil: - results = addEventOutputToTime(results, currencyStatistic.Events[i].SignalEvent.GetTime(), - fmt.Sprintf("%v %v %v %v | Price: $%v - Reason: %v", - currencyStatistic.Events[i].SignalEvent.GetTime().Format(gctcommon.SimpleTimeFormat), - currencyStatistic.Events[i].SignalEvent.GetExchange(), - currencyStatistic.Events[i].SignalEvent.GetAssetType(), - currencyStatistic.Events[i].SignalEvent.Pair(), - currencyStatistic.Events[i].SignalEvent.GetPrice().Round(8), - currencyStatistic.Events[i].SignalEvent.GetReason())) - case currencyStatistic.Events[i].DataEvent != nil: - results = addEventOutputToTime(results, currencyStatistic.Events[i].DataEvent.GetTime(), - fmt.Sprintf("%v %v %v %v | Price: $%v - Reason: %v", - currencyStatistic.Events[i].DataEvent.GetTime().Format(gctcommon.SimpleTimeFormat), - currencyStatistic.Events[i].DataEvent.GetExchange(), - currencyStatistic.Events[i].DataEvent.GetAssetType(), - currencyStatistic.Events[i].DataEvent.Pair(), - currencyStatistic.Events[i].DataEvent.GetClosePrice().Round(8), - currencyStatistic.Events[i].DataEvent.GetReason())) - default: - errs = append(errs, fmt.Errorf("%v %v %v unexpected data received %+v", exch, a, pair, currencyStatistic.Events[i])) - } - } - } - } - } - - sort.Slice(results, func(i, j int) bool { - b1 := results[i] - b2 := results[j] - return b1.Time.Before(b2.Time) - }) - for i := range results { - for j := range results[i].Events { - log.Info(common.SubLoggers[common.Statistics], results[i].Events[j]) - } - } - if len(errs) > 0 { - log.Info(common.SubLoggers[common.Statistics], "------------------Errors-------------------------------------") - for i := range errs { - log.Error(common.SubLoggers[common.Statistics], errs[i].Error()) - } - } -} - // SetStrategyName sets the name for statistical identification func (s *Statistic) SetStrategyName(name string) { s.StrategyName = name @@ -416,101 +291,3 @@ func (s *Statistic) Serialise() (string, error) { return string(resp), nil } - -// CalculateRatios creates arithmetic and geometric ratios from funding or currency pair data -func CalculateRatios(benchmarkRates, returnsPerCandle []decimal.Decimal, riskFreeRatePerCandle decimal.Decimal, maxDrawdown *Swing, logMessage string) (arithmeticStats, geometricStats *Ratios, err error) { - var arithmeticBenchmarkAverage, geometricBenchmarkAverage decimal.Decimal - arithmeticBenchmarkAverage, err = gctmath.DecimalArithmeticMean(benchmarkRates) - if err != nil { - return nil, nil, err - } - geometricBenchmarkAverage, err = gctmath.DecimalFinancialGeometricMean(benchmarkRates) - if err != nil { - return nil, nil, err - } - - riskFreeRateForPeriod := riskFreeRatePerCandle.Mul(decimal.NewFromInt(int64(len(benchmarkRates)))) - - var arithmeticReturnsPerCandle, geometricReturnsPerCandle, arithmeticSharpe, arithmeticSortino, - arithmeticInformation, arithmeticCalmar, geomSharpe, geomSortino, geomInformation, geomCalmar decimal.Decimal - - arithmeticReturnsPerCandle, err = gctmath.DecimalArithmeticMean(returnsPerCandle) - if err != nil { - return nil, nil, err - } - geometricReturnsPerCandle, err = gctmath.DecimalFinancialGeometricMean(returnsPerCandle) - if err != nil { - return nil, nil, err - } - - arithmeticSharpe, err = gctmath.DecimalSharpeRatio(returnsPerCandle, riskFreeRatePerCandle, arithmeticReturnsPerCandle) - if err != nil { - return nil, nil, err - } - arithmeticSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, arithmeticReturnsPerCandle) - if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { - if errors.Is(err, gctmath.ErrInexactConversion) { - log.Warnf(common.SubLoggers[common.Statistics], "%s funding arithmetic sortino ratio %v", logMessage, err) - } else { - return nil, nil, err - } - } - arithmeticInformation, err = gctmath.DecimalInformationRatio(returnsPerCandle, benchmarkRates, arithmeticReturnsPerCandle, arithmeticBenchmarkAverage) - if err != nil { - return nil, nil, err - } - arithmeticCalmar, err = gctmath.DecimalCalmarRatio(maxDrawdown.Highest.Value, maxDrawdown.Lowest.Value, arithmeticReturnsPerCandle, riskFreeRateForPeriod) - if err != nil { - return nil, nil, err - } - - arithmeticStats = &Ratios{} - if !arithmeticSharpe.IsZero() { - arithmeticStats.SharpeRatio = arithmeticSharpe - } - if !arithmeticSortino.IsZero() { - arithmeticStats.SortinoRatio = arithmeticSortino - } - if !arithmeticInformation.IsZero() { - arithmeticStats.InformationRatio = arithmeticInformation - } - if !arithmeticCalmar.IsZero() { - arithmeticStats.CalmarRatio = arithmeticCalmar - } - - geomSharpe, err = gctmath.DecimalSharpeRatio(returnsPerCandle, riskFreeRatePerCandle, geometricReturnsPerCandle) - if err != nil { - return nil, nil, err - } - geomSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, geometricReturnsPerCandle) - if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { - if errors.Is(err, gctmath.ErrInexactConversion) { - log.Warnf(common.SubLoggers[common.Statistics], "%s geometric sortino ratio %v", logMessage, err) - } else { - return nil, nil, err - } - } - geomInformation, err = gctmath.DecimalInformationRatio(returnsPerCandle, benchmarkRates, geometricReturnsPerCandle, geometricBenchmarkAverage) - if err != nil { - return nil, nil, err - } - geomCalmar, err = gctmath.DecimalCalmarRatio(maxDrawdown.Highest.Value, maxDrawdown.Lowest.Value, geometricReturnsPerCandle, riskFreeRateForPeriod) - if err != nil { - return nil, nil, err - } - geometricStats = &Ratios{} - if !arithmeticSharpe.IsZero() { - geometricStats.SharpeRatio = geomSharpe - } - if !arithmeticSortino.IsZero() { - geometricStats.SortinoRatio = geomSortino - } - if !arithmeticInformation.IsZero() { - geometricStats.InformationRatio = geomInformation - } - if !arithmeticCalmar.IsZero() { - geometricStats.CalmarRatio = geomCalmar - } - - return arithmeticStats, geometricStats, nil -} diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 8f1379b5c69..d03195eee68 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -136,6 +136,8 @@ type CurrencyPairStatistic struct { DoesPerformanceBeatTheMarket bool `json:"does-performance-beat-the-market"` BuyOrders int64 `json:"buy-orders"` + LongOrders int64 `json:"long-orders"` + ShortOrders int64 `json:"short-orders"` SellOrders int64 `json:"sell-orders"` TotalOrders int64 `json:"total-orders"` @@ -185,6 +187,7 @@ type Swing struct { type ValueAtTime struct { Time time.Time `json:"time"` Value decimal.Decimal `json:"value"` + Set bool `json:"-"` } type relatedCurrencyPairStatistics struct { @@ -220,19 +223,17 @@ type FundingItemStatistics struct { // Collateral stats IsCollateral bool InitialCollateral ValueAtTime - EndingCollateral ValueAtTime + FinalCollateral ValueAtTime HighestCollateral ValueAtTime LowestCollateral ValueAtTime - // PNL - LowestUPNL ValueAtTime - HighestUPNL ValueAtTime - LowestRPNL ValueAtTime - HighestRPNL ValueAtTime - FinalUPNL ValueAtTime - FinalRPNL ValueAtTime + // Contracts + LowestHoldings ValueAtTime + HighestHoldings ValueAtTime + InitialHoldings ValueAtTime + FinalHoldings ValueAtTime } -// TotalFundingStatistics holds values for overal statistics for funding items +// TotalFundingStatistics holds values for overall statistics for funding items type TotalFundingStatistics struct { HoldingValues []ValueAtTime InitialHoldingValue ValueAtTime @@ -245,6 +246,8 @@ type TotalFundingStatistics struct { CompoundAnnualGrowthRate decimal.Decimal BuyOrders int64 SellOrders int64 + LongOrders int64 + ShortOrders int64 TotalOrders int64 MaxDrawdown Swing GeometricRatios *Ratios diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 147147ebd30..bbbb1000194 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -95,22 +95,16 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf case len(pos) > 0 && v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position on last event") - spotSignal.AppendReason("no action required") case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position after reaching close short distance percentage") - spotSignal.AppendReason("no action required") case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed && fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage): futuresSignal.SetDirection(order.Short) futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) futuresSignal.AppendReason("opening position after reaching open short distance percentage") - spotSignal.AppendReason("no action required") - default: - futuresSignal.AppendReason("no action required") - spotSignal.AppendReason("no action required") } response = append(response, &spotSignal, &futuresSignal) } diff --git a/backtester/main.go b/backtester/main.go index ce9f410fd8b..6178abf70ca 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -18,7 +18,7 @@ import ( func main() { var configPath, templatePath, reportOutput string - var printLogo, generateReport, darkReport, verbose bool + var printLogo, generateReport, darkReport, verbose, colourOutput, logSubHeader bool wd, err := os.Getwd() if err != nil { fmt.Printf("Could not get working directory. Error: %v.\n", err) @@ -68,12 +68,43 @@ func main() { "verbose", false, "if enabled, will set exchange requests to verbose for debugging purposes") + flag.BoolVar( + &colourOutput, + "colouroutput", + false, + "if enabled, will print in colours, if your terminal supports \033[38;5;99m[colours like this]\u001b[0m") + flag.BoolVar( + &logSubHeader, + "logsubheader", + true, + "displays logging subheader to track where activity originates") flag.Parse() + if !colourOutput { + common.ColourGreen = "" + common.ColourWhite = "" + common.ColourGrey = "" + common.ColourDefault = "" + common.ColourH1 = "" + common.ColourH2 = "" + common.ColourH3 = "" + common.ColourH4 = "" + common.ColourSuccess = "" + common.ColourInfo = "" + common.ColourDebug = "" + common.ColourWarn = "" + common.ColourProblem = "" + common.ColourDarkGrey = "" + common.ColourError = "" + } var bt *backtest.BackTest var cfg *config.Config log.GlobalLogConfig = log.GenDefaultSettings() - log.GlobalLogConfig.AdvancedSettings.ShowLogSystemName = convert.BoolPtr(true) + log.GlobalLogConfig.AdvancedSettings.ShowLogSystemName = convert.BoolPtr(logSubHeader) + log.GlobalLogConfig.AdvancedSettings.Headers.Info = common.ColourInfo + "[INFO]" + common.ColourDefault + log.GlobalLogConfig.AdvancedSettings.Headers.Warn = common.ColourWarn + "[WARN]" + common.ColourDefault + log.GlobalLogConfig.AdvancedSettings.Headers.Debug = common.ColourDebug + "[DEBUG]" + common.ColourDefault + log.GlobalLogConfig.AdvancedSettings.Headers.Error = common.ColourError + "[ERROR]" + common.ColourDefault err = log.SetupGlobalLogger() if err != nil { fmt.Printf("Could not setup global logger. Error: %v.\n", err) @@ -98,6 +129,9 @@ func main() { os.Exit(1) } if printLogo { + for i := range common.LogoLines { + fmt.Println(common.LogoLines[i]) + } fmt.Print(common.ASCIILogo) } From 47fce537c9a391efabad52260d4ab5d4794c0a7e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 10 Mar 2022 16:06:55 +1100 Subject: [PATCH 092/171] Fixes collateral and snapshot bugs --- backtester/data/kline/kline.go | 4 +- backtester/engine/backtest.go | 2 +- .../statistics/currencystatistics.go | 50 +++++++++++++++++-- .../statistics/currencystatistics_test.go | 27 ++++++++++ .../eventhandlers/statistics/printresults.go | 25 +++++----- .../eventhandlers/statistics/statistics.go | 9 ++-- .../statistics/statistics_types.go | 17 +++++-- backtester/funding/funding.go | 10 ++-- backtester/funding/funding_types.go | 2 +- 9 files changed, 113 insertions(+), 33 deletions(-) diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index 0a3a410c872..c3bfaba186a 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -33,7 +33,7 @@ func (d *DataFromKline) Load() error { Base: event.Base{ Offset: int64(i + 1), Exchange: d.Item.Exchange, - Time: d.Item.Candles[i].Time, + Time: d.Item.Candles[i].Time.UTC(), Interval: d.Item.Interval, CurrencyPair: d.Item.Pair, AssetType: d.Item.Asset, @@ -47,7 +47,7 @@ func (d *DataFromKline) Load() error { ValidationIssues: d.Item.Candles[i].ValidationIssues, } klineData[i] = klinerino - d.addedTimes[d.Item.Candles[i].Time] = true + d.addedTimes[d.Item.Candles[i].Time.UTC()] = true } d.SetStream(klineData) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index ae2e1f64227..c5911aedb97 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -176,7 +176,7 @@ func (bt *BackTest) processSimultaneousDataEvents() error { for _, assetMap := range exchangeMap { for _, dataHandler := range assetMap { latestData := dataHandler.Latest() - funds, err := bt.Funding.GetFundingForEAP(latestData.GetExchange(), latestData.GetAssetType(), latestData.Pair()) + funds, err := bt.Funding.GetFundingForEvent(latestData) if err != nil { return err } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 0fe2ba93945..a51b98d5433 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -34,11 +34,15 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e } for i := range c.Events { price := c.Events[i].DataEvent.GetClosePrice() - if c.LowestClosePrice.IsZero() || price.LessThan(c.LowestClosePrice) { - c.LowestClosePrice = price + if price.LessThan(c.LowestClosePrice.Value) || !c.LowestClosePrice.Set { + c.LowestClosePrice.Value = price + c.LowestClosePrice.Time = c.Events[i].DataEvent.GetTime() + c.LowestClosePrice.Set = true } - if price.GreaterThan(c.HighestClosePrice) { - c.HighestClosePrice = price + if price.GreaterThan(c.HighestClosePrice.Value) { + c.HighestClosePrice.Value = price + c.HighestClosePrice.Time = c.Events[i].DataEvent.GetTime() + c.HighestClosePrice.Set = true } } @@ -49,6 +53,7 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e if first.Holdings.TotalValue.GreaterThan(decimal.Zero) { c.StrategyMovement = last.Holdings.TotalValue.Sub(first.Holdings.TotalValue).Div(first.Holdings.TotalValue).Mul(oneHundred) } + c.analysePNLGrowth() c.calculateHighestCommittedFunds() returnsPerCandle := make([]decimal.Decimal, len(c.Events)) benchmarkRates := make([]decimal.Decimal, len(c.Events)) @@ -132,3 +137,40 @@ func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() { } } } + +func (c *CurrencyPairStatistic) analysePNLGrowth() { + if !c.Asset.IsFutures() { + return + } + var lowestUnrealised, highestUnrealised, lowestRealised, highestRealised ValueAtTime + for i := range c.Events { + if c.Events[i].PNL == nil { + continue + } + if c.Events[i].PNL.Result.UnrealisedPNL.LessThan(lowestUnrealised.Value) || (!c.Events[i].PNL.Result.UnrealisedPNL.IsZero() && !lowestUnrealised.Set) { + lowestUnrealised.Value = c.Events[i].PNL.Result.UnrealisedPNL + lowestUnrealised.Time = c.Events[i].PNL.Result.Time + lowestUnrealised.Set = true + } + if c.Events[i].PNL.Result.UnrealisedPNL.GreaterThan(highestUnrealised.Value) || (!c.Events[i].PNL.Result.UnrealisedPNL.IsZero() && !highestUnrealised.Set) { + highestUnrealised.Value = c.Events[i].PNL.Result.UnrealisedPNL + highestUnrealised.Time = c.Events[i].PNL.Result.Time + highestUnrealised.Set = true + } + + if c.Events[i].PNL.Result.RealisedPNL.LessThan(lowestRealised.Value) || (!c.Events[i].PNL.Result.RealisedPNL.IsZero() && !lowestRealised.Set) { + lowestRealised.Value = c.Events[i].PNL.Result.RealisedPNL + lowestRealised.Time = c.Events[i].PNL.Result.Time + lowestRealised.Set = true + } + if c.Events[i].PNL.Result.RealisedPNL.GreaterThan(highestRealised.Value) || (!c.Events[i].PNL.Result.RealisedPNL.IsZero() && !highestRealised.Set) { + highestRealised.Value = c.Events[i].PNL.Result.RealisedPNL + highestRealised.Time = c.Events[i].PNL.Result.Time + highestRealised.Set = true + } + } + c.LowestRealisedPNL = lowestRealised + c.LowestUnrealisedPNL = lowestUnrealised + c.HighestUnrealisedPNL = highestUnrealised + c.HighestRealisedPNL = highestRealised +} diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 5586d18444b..35de2462c0c 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -6,6 +6,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" @@ -270,3 +271,29 @@ func TestCalculateHighestCommittedFunds(t *testing.T) { t.Errorf("expected %v, received %v", tt2, c.HighestCommittedFunds.Time) } } + +func TestAnalysePNLGrowth(t *testing.T) { + t.Parallel() + c := CurrencyPairStatistic{} + c.analysePNLGrowth() + if !c.HighestUnrealisedPNL.Value.IsZero() || + !c.LowestUnrealisedPNL.Value.IsZero() || + !c.LowestRealisedPNL.Value.IsZero() || + !c.HighestRealisedPNL.Value.IsZero() { + t.Error("expected unset") + } + + e := testExchange + a := asset.Spot + p := currency.NewPair(currency.BTC, currency.USDT) + + c.Events = append(c.Events, + DataAtOffset{PNL: &portfolio.PNLSummary{ + Exchange: e, + Item: a, + Pair: p, + Offset: 0, + Result: order.PNLResult{}, + }}, + ) +} diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 9d38225b807..c903696f536 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -163,9 +163,11 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. }) last := c.Events[len(c.Events)-1] first := c.Events[0] - c.StartingClosePrice = first.DataEvent.GetClosePrice() - c.EndingClosePrice = last.DataEvent.GetClosePrice() - c.TotalOrders = c.BuyOrders + c.SellOrders + c.StartingClosePrice.Value = first.DataEvent.GetClosePrice() + c.StartingClosePrice.Time = first.DataEvent.GetTime() + c.EndingClosePrice.Value = last.DataEvent.GetClosePrice() + c.EndingClosePrice.Time = last.DataEvent.GetTime() + c.TotalOrders = c.BuyOrders + c.SellOrders + c.ShortOrders + c.LongOrders last.Holdings.TotalValueLost = last.Holdings.TotalValueLostToSlippage.Add(last.Holdings.TotalValueLostToVolumeSizing) sep := fmt.Sprintf("%v %v %v |\t", fSIL(e, limit12), fSIL(a.String(), limit10), fSIL(p.String(), limit14)) currStr := fmt.Sprintf(common.ColourH1+"------------------Stats for %v %v %v------------------------------------------------------"+common.ColourDefault, e, a, p) @@ -173,11 +175,10 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. if a.IsFutures() { log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Long orders: %s", sep, convert.IntToHumanFriendlyString(c.LongOrders, ",")) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Short orders: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Unrealised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Unrealised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Realised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Realised PNL: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) - + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestUnrealisedPNL.Value, 8, ".", ","), c.HighestUnrealisedPNL.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestUnrealisedPNL.Value, 8, ".", ","), c.LowestUnrealisedPNL.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestRealisedPNL.Value, 8, ".", ","), c.HighestRealisedPNL.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestRealisedPNL.Value, 8, ".", ","), c.LowestRealisedPNL.Time) } else { log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) @@ -221,10 +222,10 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. } log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Results------------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Starting Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Finishing Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Close Price: %s", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Starting Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice.Value, 8, ".", ","), c.StartingClosePrice.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Finishing Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice.Value, 8, ".", ","), c.EndingClosePrice.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice.Value, 8, ".", ","), c.LowestClosePrice.Time) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice.Value, 8, ".", ","), c.HighestClosePrice.Time) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) if !usingExchangeLevelFunding { diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 42dc27555ac..a94f8244b58 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -33,13 +33,14 @@ func (s *Statistic) SetupEventForTime(ev common.DataEventHandler) error { s.setupMap(ex, a) lookup := s.ExchangeAssetPairStatistics[ex][a][p] if lookup == nil { - lookup = &CurrencyPairStatistic{} + lookup = &CurrencyPairStatistic{ + Exchange: ev.GetExchange(), + Asset: ev.GetAssetType(), + Currency: ev.Pair(), + } } for i := range lookup.Events { if lookup.Events[i].DataEvent.GetTime().Equal(ev.GetTime()) && - lookup.Events[i].DataEvent.GetExchange() == ev.GetExchange() && - lookup.Events[i].DataEvent.GetAssetType() == ev.GetAssetType() && - lookup.Events[i].DataEvent.Pair().Equal(ev.Pair()) && lookup.Events[i].DataEvent.GetOffset() == ev.GetOffset() { return ErrAlreadyProcessed } diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index d03195eee68..86d70a151e8 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -131,6 +131,10 @@ type DataAtOffset struct { // CurrencyPairStatistic Holds all events and statistics relevant to an exchange, asset type and currency pair type CurrencyPairStatistic struct { + Exchange string + Asset asset.Item + Currency currency.Pair + ShowMissingDataWarning bool `json:"-"` IsStrategyProfitable bool `json:"is-strategy-profitable"` DoesPerformanceBeatTheMarket bool `json:"does-performance-beat-the-market"` @@ -141,10 +145,15 @@ type CurrencyPairStatistic struct { SellOrders int64 `json:"sell-orders"` TotalOrders int64 `json:"total-orders"` - StartingClosePrice decimal.Decimal `json:"starting-close-price"` - EndingClosePrice decimal.Decimal `json:"ending-close-price"` - LowestClosePrice decimal.Decimal `json:"lowest-close-price"` - HighestClosePrice decimal.Decimal `json:"highest-close-price"` + StartingClosePrice ValueAtTime `json:"starting-close-price"` + EndingClosePrice ValueAtTime `json:"ending-close-price"` + LowestClosePrice ValueAtTime `json:"lowest-close-price"` + HighestClosePrice ValueAtTime `json:"highest-close-price"` + HighestUnrealisedPNL ValueAtTime `json:"highest-unrealised-pnl"` + LowestUnrealisedPNL ValueAtTime `json:"lowest-unrealised-pnl"` + HighestRealisedPNL ValueAtTime `json:"highest-realised-pnl"` + LowestRealisedPNL ValueAtTime `json:"lowest-realised-pnl"` + MarketMovement decimal.Decimal `json:"market-movement"` StrategyMovement decimal.Decimal `json:"strategy-movement"` UnrealisedPNL decimal.Decimal `json:"unrealised-pnl"` diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 17fc4664fc3..900a4d8f40d 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -77,7 +77,7 @@ func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, trans initialFunds: initialFunds, available: initialFunds, transferFee: transferFee, - snapshot: make(map[time.Time]ItemSnapshot), + snapshot: make(map[int64]ItemSnapshot), }, nil } @@ -114,7 +114,7 @@ func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) err func (f *FundManager) CreateSnapshot(t time.Time) { for i := range f.items { if f.items[i].snapshot == nil { - f.items[i].snapshot = make(map[time.Time]ItemSnapshot) + f.items[i].snapshot = make(map[int64]ItemSnapshot) } iss := ItemSnapshot{ Available: f.items[i].available, @@ -136,7 +136,7 @@ func (f *FundManager) CreateSnapshot(t time.Time) { iss.USDValue = usdClosePrice.Mul(f.items[i].available) } - f.items[i].snapshot[t] = iss + f.items[i].snapshot[t.Unix()] = iss } } @@ -518,8 +518,8 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { } for i := range f.items { - if !f.items[i].isCollateral && f.items[i].asset.IsFutures() { - // contracts don't contribute to collateral + if f.items[i].asset.IsFutures() { + // futures positions aren't collateral, they use it continue } exch, ok := exchMap[f.items[i].exchange] diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index f34d5b9ef4b..7c71f848e7b 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -134,7 +134,7 @@ type Item struct { transferFee decimal.Decimal pairedWith *Item trackingCandles *kline.DataFromKline - snapshot map[time.Time]ItemSnapshot + snapshot map[int64]ItemSnapshot isCollateral bool collateralCandles map[currency.Code]kline.DataFromKline } From 475ec7338919b5f1f88b79d4274ab23a871b6835 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 10 Mar 2022 16:30:08 +1100 Subject: [PATCH 093/171] Completes test --- .../statistics/currencystatistics_test.go | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 35de2462c0c..18925ef94bb 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -284,8 +284,30 @@ func TestAnalysePNLGrowth(t *testing.T) { } e := testExchange - a := asset.Spot + a := asset.Futures p := currency.NewPair(currency.BTC, currency.USDT) + c.Asset = asset.Futures + c.Events = append(c.Events, + DataAtOffset{PNL: &portfolio.PNLSummary{ + Exchange: e, + Item: a, + Pair: p, + Offset: 0, + Result: order.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1), + RealisedPNL: decimal.NewFromInt(2), + }, + }}, + ) + + c.analysePNLGrowth() + if c.HighestRealisedPNL.Value.Equal(decimal.NewFromInt(2)) { + t.Errorf("received %v expected 2", c.HighestRealisedPNL.Value) + } + if c.LowestUnrealisedPNL.Value.Equal(decimal.NewFromInt(1)) { + t.Errorf("received %v expected 1", c.LowestUnrealisedPNL.Value) + } c.Events = append(c.Events, DataAtOffset{PNL: &portfolio.PNLSummary{ @@ -293,7 +315,19 @@ func TestAnalysePNLGrowth(t *testing.T) { Item: a, Pair: p, Offset: 0, - Result: order.PNLResult{}, + Result: order.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromFloat(0.5), + RealisedPNL: decimal.NewFromInt(1), + }, }}, ) + + c.analysePNLGrowth() + if c.HighestRealisedPNL.Value.Equal(decimal.NewFromInt(2)) { + t.Errorf("received %v expected 2", c.HighestRealisedPNL.Value) + } + if c.LowestUnrealisedPNL.Value.Equal(decimal.NewFromFloat(0.5)) { + t.Errorf("received %v expected 0.5", c.LowestUnrealisedPNL.Value) + } } From fa9a597538ee8a492d62ede0b060fa54585ea76e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 11 Mar 2022 16:50:25 +1100 Subject: [PATCH 094/171] Fixes totals bug --- backtester/engine/backtest.go | 8 +++++--- backtester/eventhandlers/exchange/exchange.go | 12 ++++++------ .../strategies/ftxcashandcarry/ftxcashandcarry.go | 5 ++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index c5911aedb97..cd06e47eada 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -51,9 +51,9 @@ dataLoadingIssue: for ev := bt.EventQueue.NextEvent(); ; ev = bt.EventQueue.NextEvent() { if ev == nil { dataHandlerMap := bt.Datas.GetAllData() + var hasProcessedData bool for exchangeName, exchangeMap := range dataHandlerMap { for assetItem, assetMap := range exchangeMap { - var hasProcessedData bool for currencyPair, dataHandler := range assetMap { d := dataHandler.Next() if d == nil { @@ -63,6 +63,9 @@ dataLoadingIssue: break dataLoadingIssue } if bt.Strategy.UsingSimultaneousProcessing() && hasProcessedData { + // only append one event, as simultaneous processing + // will retrieve all relevant events to process under + // processSimultaneousDataEvents() continue } bt.EventQueue.AppendEvent(d) @@ -70,8 +73,7 @@ dataLoadingIssue: } } } - } - if ev != nil { + } else { err := bt.handleEvent(ev) if err != nil { return err diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index e833303e425..cc1819e4f03 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -20,6 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/log" ) // Reset returns the exchange to initial settings @@ -185,8 +186,6 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } return f, err } - // realising pnl for a closed futures order occurs in the - // portfolio OnFill function } ords := orderManager.GetOrdersSnapshot("") @@ -199,11 +198,11 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * ords[i].CloseTime = o.GetTime() f.Order = &ords[i] f.PurchasePrice = decimal.NewFromFloat(ords[i].Price) - if ords[i].AssetType.IsFutures() || f.GetDirection() == common.ClosePosition { - f.Total = limitReducedAmount.Add(f.ExchangeFee) - } else { - f.Total = f.PurchasePrice.Mul(limitReducedAmount).Add(f.ExchangeFee) + f.Amount = decimal.NewFromFloat(ords[i].Amount) + if ords[i].Fee > 0 { + f.ExchangeFee = decimal.NewFromFloat(ords[i].Fee) } + f.Total = f.PurchasePrice.Mul(f.Amount).Add(f.ExchangeFee) } if f.Order == nil { @@ -327,6 +326,7 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal Cost: p, FullyMatched: true, } + log.Infof(log.Global, "%v %v %v %v %v - %v %v", f.GetOffset(), f.GetExchange(), f.GetAssetType(), f.Pair(), f.GetTime(), price, amount) resp, err := orderManager.SubmitFakeOrder(o, submitResponse, useExchangeLimits) if resp != nil { orderID = resp.OrderID diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index bbbb1000194..ec0425f7ef9 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -92,21 +92,24 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf // as the futures signal relies on a completed spot order purchase // to use as collateral spotSignal.FillDependentEvent = &futuresSignal + response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position on last event") + response = append(response, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position after reaching close short distance percentage") + response = append(response, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed && fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage): futuresSignal.SetDirection(order.Short) futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) futuresSignal.AppendReason("opening position after reaching open short distance percentage") + response = append(response, &futuresSignal) } - response = append(response, &spotSignal, &futuresSignal) } return response, nil } From b9f0b9d848b82457c8bd57cbc09249ba861607b9 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 14 Mar 2022 12:18:13 +1100 Subject: [PATCH 095/171] Fix double buy, expand stats, fixes usd totals, introduce interface --- backtester/engine/backtest_types.go | 3 -- .../eventhandlers/portfolio/portfolio.go | 16 ++++++++++ .../portfolio/portfolio_types.go | 12 +++++++ .../statistics/currencystatistics.go | 32 +++++++++++-------- .../statistics/fundingstatistics.go | 9 +++--- .../eventhandlers/statistics/printresults.go | 14 +++++--- .../eventhandlers/statistics/statistics.go | 5 +-- .../statistics/statistics_types.go | 5 +-- .../ftxcashandcarry/ftxcashandcarry.go | 11 ++++--- backtester/funding/funding.go | 2 +- 10 files changed, 75 insertions(+), 34 deletions(-) diff --git a/backtester/engine/backtest_types.go b/backtester/engine/backtest_types.go index 1ef296c9bbe..248bc70513c 100644 --- a/backtester/engine/backtest_types.go +++ b/backtester/engine/backtest_types.go @@ -43,6 +43,3 @@ type BackTest struct { orderManager *engine.OrderManager databaseManager *engine.DatabaseConnectionManager } - -// TODO make this a config var -const defaultRounding = 8 diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 0978e38c03a..3121e72da60 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -639,3 +639,19 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { } return result } + +// GetUnrealisedPNL returns a basic struct containing unrealised PNL +func (p PNLSummary) GetUnrealisedPNL() BasicPNLResult { + return BasicPNLResult{ + Time: p.Result.Time, + PNL: p.Result.UnrealisedPNL, + } +} + +// GetRealisedPNL returns a basic struct containing realised PNL +func (p PNLSummary) GetRealisedPNL() BasicPNLResult { + return BasicPNLResult{ + Time: p.Result.Time, + PNL: p.Result.RealisedPNL, + } +} diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 2f28a5f2f08..b2af0373bc3 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -2,6 +2,7 @@ package portfolio import ( "errors" + "time" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" @@ -90,3 +91,14 @@ type PNLSummary struct { Offset int64 Result gctorder.PNLResult } + +// IPNL +type IPNL interface { + GetUnrealisedPNL() BasicPNLResult + GetRealisedPNL() BasicPNLResult +} + +type BasicPNLResult struct { + Time time.Time + PNL decimal.Decimal +} diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index a51b98d5433..ad35d5c2903 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -5,6 +5,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" gctcommon "github.com/thrasher-corp/gocryptotrader/common" gctmath "github.com/thrasher-corp/gocryptotrader/common/math" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -120,8 +121,8 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e c.TotalValueLostToSlippage = last.Holdings.TotalValueLostToSlippage.Round(2) c.TotalAssetValue = last.Holdings.BaseValue.Round(8) if last.PNL != nil { - c.UnrealisedPNL = last.PNL.Result.UnrealisedPNL - c.RealisedPNL = last.PNL.Result.RealisedPNL + c.UnrealisedPNL = last.PNL.GetUnrealisedPNL().PNL + c.RealisedPNL = last.PNL.GetRealisedPNL().PNL } if len(errs) > 0 { return errs @@ -147,25 +148,28 @@ func (c *CurrencyPairStatistic) analysePNLGrowth() { if c.Events[i].PNL == nil { continue } - if c.Events[i].PNL.Result.UnrealisedPNL.LessThan(lowestUnrealised.Value) || (!c.Events[i].PNL.Result.UnrealisedPNL.IsZero() && !lowestUnrealised.Set) { - lowestUnrealised.Value = c.Events[i].PNL.Result.UnrealisedPNL - lowestUnrealised.Time = c.Events[i].PNL.Result.Time + var unrealised, realised portfolio.BasicPNLResult + unrealised = c.Events[i].PNL.GetUnrealisedPNL() + realised = c.Events[i].PNL.GetRealisedPNL() + if unrealised.PNL.LessThan(lowestUnrealised.Value) || (!unrealised.PNL.IsZero() && !lowestUnrealised.Set) { + lowestUnrealised.Value = unrealised.PNL + lowestUnrealised.Time = unrealised.Time lowestUnrealised.Set = true } - if c.Events[i].PNL.Result.UnrealisedPNL.GreaterThan(highestUnrealised.Value) || (!c.Events[i].PNL.Result.UnrealisedPNL.IsZero() && !highestUnrealised.Set) { - highestUnrealised.Value = c.Events[i].PNL.Result.UnrealisedPNL - highestUnrealised.Time = c.Events[i].PNL.Result.Time + if unrealised.PNL.GreaterThan(highestUnrealised.Value) || (!unrealised.PNL.IsZero() && !highestUnrealised.Set) { + highestUnrealised.Value = unrealised.PNL + highestUnrealised.Time = unrealised.Time highestUnrealised.Set = true } - if c.Events[i].PNL.Result.RealisedPNL.LessThan(lowestRealised.Value) || (!c.Events[i].PNL.Result.RealisedPNL.IsZero() && !lowestRealised.Set) { - lowestRealised.Value = c.Events[i].PNL.Result.RealisedPNL - lowestRealised.Time = c.Events[i].PNL.Result.Time + if realised.PNL.LessThan(lowestRealised.Value) || (!realised.PNL.IsZero() && !lowestRealised.Set) { + lowestRealised.Value = realised.PNL + lowestRealised.Time = realised.Time lowestRealised.Set = true } - if c.Events[i].PNL.Result.RealisedPNL.GreaterThan(highestRealised.Value) || (!c.Events[i].PNL.Result.RealisedPNL.IsZero() && !highestRealised.Set) { - highestRealised.Value = c.Events[i].PNL.Result.RealisedPNL - highestRealised.Time = c.Events[i].PNL.Result.Time + if realised.PNL.GreaterThan(highestRealised.Value) || (!realised.PNL.IsZero() && !highestRealised.Set) { + lowestRealised.Value = realised.PNL + lowestRealised.Time = realised.Time highestRealised.Set = true } } diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index 1fc832f2454..5a29613c98d 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -56,11 +56,12 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str RiskFreeRate: riskFreeRate, } for i := range response.Items { - if !response.Items[i].IsCollateral { - usdStats.TotalOrders += response.Items[i].TotalOrders - usdStats.BuyOrders += response.Items[i].BuyOrders - usdStats.SellOrders += response.Items[i].SellOrders + if response.Items[i].IsCollateral { + continue } + usdStats.TotalOrders += response.Items[i].TotalOrders + usdStats.BuyOrders += response.Items[i].BuyOrders + usdStats.SellOrders += response.Items[i].SellOrders } for k, v := range report.USDTotalsOverTime { if usdStats.HighestHoldingValue.Value.LessThan(v.USDValue) { diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index c903696f536..51f4265d747 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -6,6 +6,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/currency" @@ -38,6 +39,8 @@ func (s *Statistic) PrintTotalResults() { log.Info(common.SubLoggers[common.Statistics], common.ColourH3+"------------------Orders-------------------------------------"+common.ColourDefault) log.Infof(common.SubLoggers[common.Statistics], "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) log.Infof(common.SubLoggers[common.Statistics], "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) + log.Infof(common.SubLoggers[common.Statistics], "Total long orders: %v", convert.IntToHumanFriendlyString(s.TotalLongOrders, ",")) + log.Infof(common.SubLoggers[common.Statistics], "Total short orders: %v", convert.IntToHumanFriendlyString(s.TotalShortOrders, ",")) log.Infof(common.SubLoggers[common.Statistics], "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) if s.BiggestDrawdown != nil { @@ -182,10 +185,10 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. } else { log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ","), last.Holdings.Timestamp) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ","), last.Holdings.Timestamp) log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) } log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) @@ -247,8 +250,11 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. } if last.PNL != nil { - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Unrealised PNL: %s", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.UnrealisedPNL, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Realised PNL: %s", sep, convert.DecimalToHumanFriendlyString(last.PNL.Result.RealisedPNL, 8, ".", ",")) + var unrealised, realised portfolio.BasicPNLResult + unrealised = last.PNL.GetUnrealisedPNL() + realised = last.PNL.GetRealisedPNL() + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Unrealised PNL: %s", sep, convert.DecimalToHumanFriendlyString(unrealised.PNL, 8, ".", ",")) + log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Realised PNL: %s", sep, convert.DecimalToHumanFriendlyString(realised.PNL, 8, ".", ",")) } if len(errs) > 0 { log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index a94f8244b58..82a3b904c91 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -202,8 +202,11 @@ func (s *Statistic) CalculateAllResults() error { MarketMovement: stats.MarketMovement, StrategyMovement: stats.StrategyMovement, }) + s.TotalLongOrders += stats.LongOrders + s.TotalShortOrders += stats.ShortOrders s.TotalBuyOrders += stats.BuyOrders s.TotalSellOrders += stats.SellOrders + s.TotalOrders += stats.TotalOrders if stats.ShowMissingDataWarning { s.WasAnyDataMissing = true } @@ -218,8 +221,6 @@ func (s *Statistic) CalculateAllResults() error { if err != nil { return err } - - s.TotalOrders = s.TotalBuyOrders + s.TotalSellOrders if currCount > 1 { s.BiggestDrawdown = s.GetTheBiggestDrawdownAcrossCurrencies(finalResults) s.BestMarketMovement = s.GetBestMarketPerformer(finalResults) diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 86d70a151e8..743d166d73c 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -43,6 +43,8 @@ type Statistic struct { RiskFreeRate decimal.Decimal `json:"risk-free-rate"` ExchangeAssetPairStatistics map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic `json:"exchange-asset-pair-statistics"` TotalBuyOrders int64 `json:"total-buy-orders"` + TotalLongOrders int64 `json:"total-long-orders"` + TotalShortOrders int64 `json:"total-short-orders"` TotalSellOrders int64 `json:"total-sell-orders"` TotalOrders int64 `json:"total-orders"` BiggestDrawdown *FinalResultsHolder `json:"biggest-drawdown,omitempty"` @@ -125,8 +127,7 @@ type DataAtOffset struct { SignalEvent signal.Event OrderEvent order.Event FillEvent fill.Event - // TODO: consider moving this to an interface so we aren't tied to this summary type - PNL *portfolio.PNLSummary + PNL portfolio.IPNL } // CurrencyPairStatistic Holds all events and statistics relevant to an exchange, asset type and currency pair diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index ec0425f7ef9..1e07df53586 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -92,23 +92,26 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf // as the futures signal relies on a completed spot order purchase // to use as collateral spotSignal.FillDependentEvent = &futuresSignal - response = append(response, &spotSignal, &futuresSignal) + // only appending spotSignal as futuresSignal will be raised later + response = append(response, &spotSignal) case len(pos) > 0 && v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position on last event") - response = append(response, &futuresSignal) + response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("closing position after reaching close short distance percentage") - response = append(response, &futuresSignal) + response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed && fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage): futuresSignal.SetDirection(order.Short) futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) futuresSignal.AppendReason("opening position after reaching open short distance percentage") - response = append(response, &futuresSignal) + response = append(response, &spotSignal, &futuresSignal) + default: + response = append(response, &spotSignal, &futuresSignal) } } return response, nil diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 900a4d8f40d..b2436443b4f 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -303,7 +303,7 @@ func (f *FundManager) GenerateReport() *Report { var pricingOverTime []ItemSnapshot for _, v := range f.items[i].snapshot { pricingOverTime = append(pricingOverTime, v) - if !f.disableUSDTracking { + if !f.disableUSDTracking && !f.items[i].isCollateral { usdTotalForPeriod := report.USDTotalsOverTime[v.Time] usdTotalForPeriod.Time = v.Time usdTotalForPeriod.USDValue = usdTotalForPeriod.USDValue.Add(v.USDValue) From e8e844e7f2c1f48caca6016e29006b0c424f0bce Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 14 Mar 2022 16:07:49 +1100 Subject: [PATCH 096/171] Begins report formatting and calculations --- backtester/common/common.go | 8 +- backtester/engine/backtest.go | 5 + backtester/eventhandlers/statistics/common.go | 4 +- .../statistics/fundingstatistics.go | 26 +- .../eventhandlers/statistics/statistics.go | 3 + .../statistics/statistics_types.go | 1 + backtester/funding/funding.go | 14 +- backtester/report/tpl.gohtml | 1433 +++++++++-------- 8 files changed, 778 insertions(+), 716 deletions(-) diff --git a/backtester/common/common.go b/backtester/common/common.go index c6d2154ef02..d03ed1c9d62 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -17,9 +17,9 @@ func DataTypeToInt(dataType string) (int64, error) { } } -// FitStringInLimit ensures a string is within the limit -// it also helps elongate a string to fit the limit -func FitStringInLimit(str, spacer string, limit int, upper bool) string { +// FitStringToLimit ensures a string is of the length of the limit +// either by truncating the string with ellipses or padding with the spacer +func FitStringToLimit(str, spacer string, limit int, upper bool) string { limResp := limit - len(str) if upper { str = strings.ToUpper(str) @@ -39,5 +39,5 @@ func FitStringInLimit(str, spacer string, limit int, upper bool) string { } } - return str + return str[0:limit] } diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index cd06e47eada..6056cdc8861 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -334,6 +334,11 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) fde := ev.GetFillDependentEvent() if !fde.IsNil() { // some events can only be triggered on a successful fill event + fde.SetOffset(ev.GetOffset()) + err = bt.Statistic.SetEventForOffset(fde) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", fde.GetExchange(), fde.GetAssetType(), fde.Pair(), err) + } bt.EventQueue.AppendEvent(fde) } if ev.GetAssetType().IsFutures() { diff --git a/backtester/eventhandlers/statistics/common.go b/backtester/eventhandlers/statistics/common.go index c007134cc4e..1171f5cefb7 100644 --- a/backtester/eventhandlers/statistics/common.go +++ b/backtester/eventhandlers/statistics/common.go @@ -12,10 +12,10 @@ import ( "github.com/thrasher-corp/gocryptotrader/log" ) -// fSIL shorthand wrapper for FitStringInLimit +// fSIL shorthand wrapper for FitStringToLimit func fSIL(str string, limit int) string { spacer := " " - return common.FitStringInLimit(str, spacer, limit, true) + return common.FitStringToLimit(str, spacer, limit, true) } // CalculateBiggestEventDrawdown calculates the biggest drawdown using a slice of DataEvents diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index 5a29613c98d..e7c0ef92a9a 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -156,6 +156,10 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f return item, nil } closePrices := reportItem.Snapshots + item.IsCollateral = reportItem.IsCollateral + if item.IsCollateral { + return item, nil + } if len(closePrices) == 0 { return nil, errMissingSnapshots } @@ -182,19 +186,19 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f item.IsCollateral = reportItem.IsCollateral if reportItem.Asset.IsFutures() { var lowest, highest, initial, final ValueAtTime - initial.Value = reportItem.Snapshots[0].Available - initial.Time = reportItem.Snapshots[0].Time - final.Value = reportItem.Snapshots[len(reportItem.Snapshots)-1].Available - final.Time = reportItem.Snapshots[len(reportItem.Snapshots)-1].Time - for i := range reportItem.Snapshots { - if reportItem.Snapshots[i].Available.LessThan(lowest.Value) || !lowest.Set { - lowest.Value = reportItem.Snapshots[i].Available - lowest.Time = reportItem.Snapshots[i].Time + initial.Value = closePrices[0].Available + initial.Time = closePrices[0].Time + final.Value = closePrices[len(closePrices)-1].Available + final.Time = closePrices[len(closePrices)-1].Time + for i := range closePrices { + if closePrices[i].Available.LessThan(lowest.Value) || !lowest.Set { + lowest.Value = closePrices[i].Available + lowest.Time = closePrices[i].Time lowest.Set = true } - if reportItem.Snapshots[i].Available.GreaterThan(highest.Value) || !lowest.Set { - highest.Value = reportItem.Snapshots[i].Available - highest.Time = reportItem.Snapshots[i].Time + if closePrices[i].Available.GreaterThan(highest.Value) || !lowest.Set { + highest.Value = closePrices[i].Available + highest.Time = closePrices[i].Time highest.Set = true } } diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 82a3b904c91..f1c2f3cc0b7 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -183,6 +183,9 @@ func (s *Statistic) CalculateAllResults() error { for pair, stats := range assetMap { currCount++ last := stats.Events[len(stats.Events)-1] + if last.PNL != nil { + s.HasCollateral = true + } err = stats.CalculateResults(s.RiskFreeRate) if err != nil { log.Error(common.SubLoggers[common.Statistics], err) diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 743d166d73c..295ef0a3e3a 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -54,6 +54,7 @@ type Statistic struct { WasAnyDataMissing bool `json:"was-any-data-missing"` FundingStatistics *FundingStatistics `json:"funding-statistics"` FundManager funding.IFundingManager `json:"-"` + HasCollateral bool `json:"has-collateral"` } // FinalResultsHolder holds important stats about a currency's performance diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index b2436443b4f..e90f24cabd5 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -302,12 +302,14 @@ func (f *FundManager) GenerateReport() *Report { var pricingOverTime []ItemSnapshot for _, v := range f.items[i].snapshot { - pricingOverTime = append(pricingOverTime, v) - if !f.disableUSDTracking && !f.items[i].isCollateral { - usdTotalForPeriod := report.USDTotalsOverTime[v.Time] - usdTotalForPeriod.Time = v.Time - usdTotalForPeriod.USDValue = usdTotalForPeriod.USDValue.Add(v.USDValue) - report.USDTotalsOverTime[v.Time] = usdTotalForPeriod + if !f.items[i].isCollateral { + pricingOverTime = append(pricingOverTime, v) + if !f.disableUSDTracking { + usdTotalForPeriod := report.USDTotalsOverTime[v.Time] + usdTotalForPeriod.Time = v.Time + usdTotalForPeriod.USDValue = usdTotalForPeriod.USDValue.Add(v.USDValue) + report.USDTotalsOverTime[v.Time] = usdTotalForPeriod + } } } sort.Slice(pricingOverTime, func(i, j int) bool { diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 85950fe9526..5e5aecb9956 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1,3 +1,4 @@ + {{.Config.Nickname}} Results @@ -88,9 +89,9 @@ Config Settings {{ if .Warnings}} - + {{end}} - + @@ -132,101 +133,103 @@

Results for {{.Statistics.StrategyName}} {{.Config.Nickname }}

-
-
-

Executive Summary

-
-
-

Goal

-

{{.Config.Goal}}

-
Strategy Description
-

{{.Statistics.StrategyDescription}}

- {{ if or .Config.DataSettings.APIData .Config.DataSettings.DatabaseData }} - - - {{ if .Config.DataSettings.APIData}} - - - - - - - - - - - - - - {{end}} - {{ if .Config.DataSettings.DatabaseData}} - - - - - - - - - - - - - - {{if .Statistics.WasAnyDataMissing}} - - - - - {{end}} +
+
+

Executive Summary

+
+
+

Goal

+

{{.Config.Goal}}

+
Strategy Description
+

{{.Statistics.StrategyDescription}}

+ {{ if or .Config.DataSettings.APIData .Config.DataSettings.DatabaseData }} +
Start Date - {{.Config.DataSettings.APIData.StartDate}} -
End Date - {{.Config.DataSettings.APIData.EndDate}} -
Interval - {{.Config.DataSettings.Interval}} -
Start Date - {{.Config.DataSettings.DatabaseData.StartDate}} -
End Date - {{.Config.DataSettings.DatabaseData.EndDate}} -
Interval - {{.Config.DataSettings.Interval}} -
Was any data missing?{{ .Statistics.WasAnyDataMissing}}
+ + {{ if .Config.DataSettings.APIData}} + + + + + + - {{end}} - -
Start Date + {{.Config.DataSettings.APIData.StartDate}} +
End Date
- {{ end }} - {{ if or .Config.DataSettings.CSVData .Config.DataSettings.LiveData }} - - + + - -
+ {{.Config.DataSettings.APIData.EndDate}} +
Interval {{.Config.DataSettings.Interval}}
- {{ end }} - {{if .Config.StrategySettings.UseExchangeLevelFunding}} -
Funding results
- - + {{end}} + {{ if .Config.DataSettings.DatabaseData}} - - - - - - {{ if eq .Config.StrategySettings.DisableUSDTracking false }} - - - {{end}} - + + - - - {{ range .Statistics.FundingStatistics.Report.Items}} + + + + + + + + + + {{if .Statistics.WasAnyDataMissing}} + + + + + {{end}} + + {{end}} + +
ExchangeAssetCurrencyInitial FundsFinal FundsInitial Fund in USDFinal Funds in USDDifferenceStart Date + {{.Config.DataSettings.DatabaseData.StartDate}} +
End Date + {{.Config.DataSettings.DatabaseData.EndDate}} +
Interval + {{.Config.DataSettings.Interval}} +
Was any data missing?{{ .Statistics.WasAnyDataMissing}}
+ {{ end }} + {{ if or .Config.DataSettings.CSVData .Config.DataSettings.LiveData }} + + + + + + + +
Interval + {{.Config.DataSettings.Interval}} +
+ {{ end }} + {{if .Config.StrategySettings.UseExchangeLevelFunding}} +
Funding results
+ + + + + + + + + {{ if eq .Config.StrategySettings.DisableUSDTracking false }} + + + {{end}} + + + + + {{ range .Statistics.FundingStatistics.Report.Items}} + {{ if .IsCollateral}} + {{else }} @@ -244,131 +247,311 @@ {{ end }} {{end}} - -
ExchangeAssetCurrencyInitial FundsFinal FundsInitial Fund in USDFinal Funds in USDDifference
{{.Exchange}} {{.Asset}}
- -
Pair market movement
+ {{end}} + + + {{ if .Statistics.HasCollateral}} +
Collateral details
- + + + {{ if eq .Config.StrategySettings.DisableUSDTracking false }} + + + {{end}} + - {{ range $exchange, $unused := .Statistics.ExchangeAssetPairStatistics}} - {{ range $asset, $unused := .}} - {{ range $pair, $unused := .}} - - - - - - - {{end}} + {{ range .Statistics.FundingStatistics.Report.Items}} + {{ if .IsCollateral}} + + + + + + + {{ if eq $.Config.StrategySettings.DisableUSDTracking false }} + + + {{ end }} + {{if .ShowInfinite}} + + {{ else }} + + {{ end }} + + {{else }} + {{end}} {{end}}
Exchange Asset CurrencyMarket MovementInitial FundsFinal FundsInitial Fund in USDFinal Funds in USDDifference
{{ $exchange}}{{ $asset}}{{ $pair}}{{ $.Prettify.Decimal8 .MarketMovement}}%
{{.Exchange}}{{.Asset}}{{.Currency}}{{ $.Prettify.Decimal8 .InitialFunds}} {{.Currency}}{{ $.Prettify.Decimal8 .FinalFunds }} {{.Currency}}${{ $.Prettify.Decimal2 .USDInitialFunds}}${{ $.Prettify.Decimal2 .USDFinalFunds}}Infinity%{{ .Difference}}%
- {{else}} -
Pair market movement
- - - - - - - - - - - - - - - - - - - - {{ range $exchange, $unused := .Statistics.ExchangeAssetPairStatistics}} - {{ range $asset, $unused := .}} - {{ range $pair, $unused := .}} - - - - - - - - - - - - - - - - {{end}} + {{ end }} + +
Pair market movement
+
ExchangeAssetCurrencyInitial Base fundsInitial Quote fundsInitial Total valueResulting Base fundsResulting Quote fundsResulting Total valueDid it make profit?Did it beat the market?Strategy MovementMarket Movement
{{ $exchange}}{{ $asset}}{{ $pair}}{{ $.Prettify.Decimal8 .InitialHoldings.BaseInitialFunds }} {{.FinalHoldings.Pair.Base}}{{ $.Prettify.Decimal8 .InitialHoldings.QuoteInitialFunds }} {{.FinalHoldings.Pair.Quote}}{{ $.Prettify.Decimal8 .InitialHoldings.TotalInitialValue }} {{.FinalHoldings.Pair.Quote}}{{ $.Prettify.Decimal8 .FinalHoldings.BaseSize }} {{ .FinalHoldings.Pair.Base}}{{ $.Prettify.Decimal8 .FinalHoldings.QuoteSize }} {{ .FinalHoldings.Pair.Quote}}{{ $.Prettify.Decimal8 .FinalHoldings.TotalValue }} {{ .FinalHoldings.Pair.Quote}}{{ .IsStrategyProfitable }} {{ .DoesPerformanceBeatTheMarket }}{{ $.Prettify.Decimal8 .StrategyMovement }}%{{ $.Prettify.Decimal8 .MarketMovement}}%
+ + + + + + + + + + {{ range $exchange, $unused := .Statistics.ExchangeAssetPairStatistics}} + {{ range $asset, $unused := .}} + {{ range $pair, $unused := .}} + + + + + + {{end}} {{end}} - -
ExchangeAssetCurrencyMarket Movement
{{ $exchange}}{{ $asset}}{{ $pair}}{{ $.Prettify.Decimal8 .MarketMovement}}%
- {{end}} - {{ if eq .Config.StrategySettings.DisableUSDTracking false}} -
USD Totals
- - - - - - - - - - + {{end}} + +
Initial Total Funds in USD${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.TotalUSDStatistics.InitialHoldingValue.Value}}
Final Total Funds in USD${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.TotalUSDStatistics.FinalHoldingValue.Value}}
+ {{else}} +
Pair market movement
+ + + + + + + + + + + + + + + + + + + + {{ range $exchange, $unused := .Statistics.ExchangeAssetPairStatistics}} + {{ range $asset, $unused := .}} + {{ range $pair, $unused := .}} + + + + + + + + + + + + + + + + {{end}} + {{end}} + {{end}} + +
ExchangeAssetCurrencyInitial Base fundsInitial Quote fundsInitial Total valueResulting Base fundsResulting Quote fundsResulting Total valueDid it make profit?Did it beat the market?Strategy MovementMarket Movement
{{ $exchange}}{{ $asset}}{{ $pair}}{{ $.Prettify.Decimal8 .InitialHoldings.BaseInitialFunds }} {{.FinalHoldings.Pair.Base}}{{ $.Prettify.Decimal8 .InitialHoldings.QuoteInitialFunds }} {{.FinalHoldings.Pair.Quote}}{{ $.Prettify.Decimal8 .InitialHoldings.TotalInitialValue }} {{.FinalHoldings.Pair.Quote}}{{ $.Prettify.Decimal8 .FinalHoldings.BaseSize }} {{ .FinalHoldings.Pair.Base}}{{ $.Prettify.Decimal8 .FinalHoldings.QuoteSize }} {{ .FinalHoldings.Pair.Quote}}{{ $.Prettify.Decimal8 .FinalHoldings.TotalValue }} {{ .FinalHoldings.Pair.Quote}}{{ .IsStrategyProfitable }} {{ .DoesPerformanceBeatTheMarket }}{{ $.Prettify.Decimal8 .StrategyMovement }}%{{ $.Prettify.Decimal8 .MarketMovement}}%
+ {{end}} + {{ if eq .Config.StrategySettings.DisableUSDTracking false}} +
USD Totals
+ + + + + + + + + + + + + + + +
Initial Total Funds in USD${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.TotalUSDStatistics.InitialHoldingValue.Value}}
Final Total Funds in USD${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.TotalUSDStatistics.FinalHoldingValue.Value}}
Difference{{ $.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.HoldingValueDifference}}%
+ {{end}} +
+
+
+
+

Config

+
+
+
+
+

Strategy Settings

+
+
+ + + + + + + + + + + + + + + +
Strategy name{{.Config.StrategySettings.Name}}
Is multi currency{{.Config.StrategySettings.SimultaneousSignalProcessing}}
Custom settings{{.Config.StrategySettings.CustomSettings}}
+
+
+
+
+

Currency Settings

+
+
+ + + + + + + + + + + + + + + + + + + + + {{ range .Config.CurrencySettings}} + {{if .USDTrackingPair}} + {{else}} - - + + + + + + + + + + + + + + - -
Exchange NameAssetCurrency BaseCurrency QuoteBuy side Min AmountBuy side Max AmountBuy side Max TotalSell side Min AmountSell side Max AmountSell side Max TotalMin Slippage PercentMax Slippage PercentTaker FeeMaximum Holdings Ratio
Difference{{ $.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.HoldingValueDifference}}%{{.ExchangeName}}{{.Asset}}{{.Base}}{{.Quote}}{{ $.Prettify.Decimal64 .BuySide.MinimumSize}} {{.Base}}{{ $.Prettify.Decimal64 .BuySide.MaximumSize}} {{.Base}}{{ $.Prettify.Decimal64 .BuySide.MaximumTotal}} {{.Quote}}{{ $.Prettify.Decimal64 .SellSide.MinimumSize}} {{.Base}}{{ $.Prettify.Decimal64 .SellSide.MaximumSize}} {{.Base}}{{ $.Prettify.Decimal64 .SellSide.MaximumTotal}} {{.Quote}}{{ $.Prettify.Decimal64 .MinimumSlippagePercent}}%{{ $.Prettify.Decimal64 .MaximumSlippagePercent}}%{{.TakerFee}}{{.MaximumHoldingsRatio}}
+ {{end}} {{end}} -
-
-
-
-

Config

-
+ +
+ + {{ if .Statistics.FundingStatistics.Report }}
-

Strategy Settings

+

Funding Settings

- - - - - - - - - + - - + + + + + + + + + {{ range .Statistics.FundingStatistics.Report.Items}} + + + + + + + + + {{end}}
Strategy name{{.Config.StrategySettings.Name}}
Is multi currency{{.Config.StrategySettings.SimultaneousSignalProcessing}}
Custom settings{{.Config.StrategySettings.CustomSettings}}Exchange NameAssetCurrencyPaired WithInitial FundsTransfer Fee
{{.Exchange}}{{.Asset}}{{.Currency}}{{.PairedWith}}{{ $.Prettify.Decimal8 .InitialFunds}}{{ $.Prettify.Decimal64 .TransferFee}}
-
-
-

Currency Settings

+ {{ end }} +
+
+

Portfolio Settings

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Can Use LeverageMax Leverage RateMax Orders With Leverage RatioBuy side Min AmountBuy side Max AmountBuy side Max TotalSell side Min AmountSell side Max AmountSell side Max Total
{{.Config.PortfolioSettings.Leverage.CanUseLeverage}}{{.Config.PortfolioSettings.Leverage.MaximumOrderLeverageRate}}{{.Config.PortfolioSettings.Leverage.MaximumOrdersWithLeverageRatio}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MinimumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumTotal}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.SellSide.MinimumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.SellSide.MaximumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.SellSide.MaximumTotal}}
+
+
+
+
+

Statistics Settings

+
+
+ + + + + + + + + + + +
Risk-Free Rate
{{ .Config.StatisticSettings.RiskFreeRate}}
+
+ {{ if .Warnings }} +
+

Warnings

@@ -378,173 +561,37 @@ - - - - - - - - - - + {{ range .Config.CurrencySettings}} {{if .USDTrackingPair}} {{else}} - - - - - - - - - - - - - - - - + {{if .ShowExchangeOrderLimitWarning}} + + + + + + + + {{end}} + {{ range $.Warnings}} + + + + + + + + {{end}} {{end}} {{end}}
Asset Currency Base Currency QuoteBuy side Min AmountBuy side Max AmountBuy side Max TotalSell side Min AmountSell side Max AmountSell side Max TotalMin Slippage PercentMax Slippage PercentTaker FeeMaximum Holdings RatioWarning
{{.ExchangeName}}{{.Asset}}{{.Base}}{{.Quote}}{{ $.Prettify.Decimal64 .BuySide.MinimumSize}} {{.Base}}{{ $.Prettify.Decimal64 .BuySide.MaximumSize}} {{.Base}}{{ $.Prettify.Decimal64 .BuySide.MaximumTotal}} {{.Quote}}{{ $.Prettify.Decimal64 .SellSide.MinimumSize}} {{.Base}}{{ $.Prettify.Decimal64 .SellSide.MaximumSize}} {{.Base}}{{ $.Prettify.Decimal64 .SellSide.MaximumTotal}} {{.Quote}}{{ $.Prettify.Decimal64 .MinimumSlippagePercent}}%{{ $.Prettify.Decimal64 .MaximumSlippagePercent}}%{{.TakerFee}}{{.MaximumHoldingsRatio}}
{{.ExchangeName}}{{.Asset}}{{.Base}}{{.Quote}}order execution limits supported but disabled, live results may differ
{{.Exchange}}{{.Asset}}{{.Pair.Base}}{{.Pair.Quote}}{{.Message}}
-
- {{ if .Statistics.FundingStatistics.Report }} -
-
-

Funding Settings

-
-
- - - - - - - - - - - - - {{ range .Statistics.FundingStatistics.Report.Items}} - - - - - - - - - {{end}} - -
Exchange NameAssetCurrencyPaired WithInitial FundsTransfer Fee
{{.Exchange}}{{.Asset}}{{.Currency}}{{.PairedWith}}{{ $.Prettify.Decimal8 .InitialFunds}}{{ $.Prettify.Decimal64 .TransferFee}}
-
-
- {{ end }} -
-
-

Portfolio Settings

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Can Use LeverageMax Leverage RateMax Orders With Leverage RatioBuy side Min AmountBuy side Max AmountBuy side Max TotalSell side Min AmountSell side Max AmountSell side Max Total
{{.Config.PortfolioSettings.Leverage.CanUseLeverage}}{{.Config.PortfolioSettings.Leverage.MaximumOrderLeverageRate}}{{.Config.PortfolioSettings.Leverage.MaximumOrdersWithLeverageRatio}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MinimumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumTotal}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.SellSide.MinimumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.SellSide.MaximumSize}}{{ $.Prettify.Decimal64 .Config.PortfolioSettings.SellSide.MaximumTotal}}
-
-
-
-
-

Statistics Settings

-
-
- - - - - - - - - - - -
Risk-Free Rate
{{ .Config.StatisticSettings.RiskFreeRate}}
-
- {{ if .Warnings }} -
-

Warnings

-
-
- - - - - - - - - - - - {{ range .Config.CurrencySettings}} - {{if .USDTrackingPair}} - {{else}} - {{if .ShowExchangeOrderLimitWarning}} - - - - - - - - {{end}} - {{ range $.Warnings}} - - - - - - - - {{end}} - {{end}} - {{end}} - -
Exchange NameAssetCurrency BaseCurrency QuoteWarning
{{.ExchangeName}}{{.Asset}}{{.Base}}{{.Quote}}order execution limits supported but disabled, live results may differ
{{.Exchange}}{{.Asset}}{{.Pair.Base}}{{.Pair.Quote}}{{.Message}}
-
- {{end}} + {{end}}

Charts

@@ -789,7 +836,7 @@ pointInterval: {{$.Statistics.CandleInterval.Duration.Milliseconds}}, data: [ {{ range .DataPoints }} - [{{.UnixMilli}}, {{.Value}}], + [{{.UnixMilli}}, {{.Value}}], {{end}} ] }, @@ -856,7 +903,7 @@ name: {{.Name}}, data: [ {{ range .DataPoints }} - [{{.UnixMilli}},{{.Value}}], + [{{.UnixMilli}},{{.Value}}], {{end}} ] }, @@ -997,254 +1044,254 @@
-
-
-

Strategy Statistics

-
-
- - - - - - - - - - +
+
+

Strategy Statistics

+
+
+
Strategy Name{{.Statistics.StrategyName}}
Risk Free Rate{{.Statistics.RiskFreeRate}}%
+ + + + + + + + + + + + + + + + + + + + + + {{ if .Statistics.BiggestDrawdown}} - - + + + {{end}} + {{ if .Statistics.BestMarketMovement}} - - + + + {{end}} + {{ if .Statistics.BestStrategyResults}} - - + + - {{ if .Statistics.BiggestDrawdown}} - - - - - {{end}} - {{ if .Statistics.BestMarketMovement}} - - - - - {{end}} - {{ if .Statistics.BestStrategyResults}} - - - - - {{ end}} - -
Strategy Name{{.Statistics.StrategyName}}
Risk Free Rate{{.Statistics.RiskFreeRate}}%
Total Buy Orders{{ $.Prettify.Int .Statistics.TotalBuyOrders}}
Total Sell Orders{{$.Prettify.Int .Statistics.TotalSellOrders}}
Total Orders{{$.Prettify.Int .Statistics.TotalOrders}}
Total Buy Orders{{ $.Prettify.Int .Statistics.TotalBuyOrders}}Biggest DrawdownStart: {{.Statistics.BiggestDrawdown.MaxDrawdown.Highest.Time }} End: {{.Statistics.BiggestDrawdown.MaxDrawdown.Lowest.Time }} Drop: {{ $.Prettify.Decimal2 .Statistics.BiggestDrawdown.MaxDrawdown.DrawdownPercent}}%
Total Sell Orders{{$.Prettify.Int .Statistics.TotalSellOrders}}Best performing market movement{{.Statistics.BestMarketMovement.Exchange }} {{.Statistics.BestMarketMovement.Asset}} {{.Statistics.BestMarketMovement.Pair}} {{ $.Prettify.Decimal2 .Statistics.BestMarketMovement.MarketMovement}}%
Total Orders{{$.Prettify.Int .Statistics.TotalOrders}}Best performing strategy movement{{.Statistics.BestStrategyResults.Exchange }} {{.Statistics.BestStrategyResults.Asset}} {{.Statistics.BestStrategyResults.Pair}} {{ $.Prettify.Decimal2 .Statistics.BestStrategyResults.StrategyMovement}}%
Biggest DrawdownStart: {{.Statistics.BiggestDrawdown.MaxDrawdown.Highest.Time }} End: {{.Statistics.BiggestDrawdown.MaxDrawdown.Lowest.Time }} Drop: {{ $.Prettify.Decimal2 .Statistics.BiggestDrawdown.MaxDrawdown.DrawdownPercent}}%
Best performing market movement{{.Statistics.BestMarketMovement.Exchange }} {{.Statistics.BestMarketMovement.Asset}} {{.Statistics.BestMarketMovement.Pair}} {{ $.Prettify.Decimal2 .Statistics.BestMarketMovement.MarketMovement}}%
Best performing strategy movement{{.Statistics.BestStrategyResults.Exchange }} {{.Statistics.BestStrategyResults.Asset}} {{.Statistics.BestStrategyResults.Pair}} {{ $.Prettify.Decimal2 .Statistics.BestStrategyResults.StrategyMovement}}%
-
+ {{ end}} + +
- {{ range $exchange, $unused := .Statistics.ExchangeAssetPairStatistics}} - {{ range $asset, $unused := .}} - {{ range $pair, $val := .}} -
-
-

Pair Statistics for {{$exchange}} {{ $asset}} {{ $pair}}

-
-
- - - - - - - - - - + + {{ range $exchange, $unused := .Statistics.ExchangeAssetPairStatistics}} + {{ range $asset, $unused := .}} + {{ range $pair, $val := .}} +
+
+

Pair Statistics for {{$exchange}} {{ $asset}} {{ $pair}}

+
+
+
Base Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseInitialFunds}} {{$val.FinalHoldings.Pair.Base}}
Quote Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteInitialFunds}} {{$val.FinalHoldings.Pair.Quote}}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ if $val.MaxDrawdown.Highest.Value.IsZero }} + {{else}} - - - - - - - - - - - - - - - - - - + + + {{ end }} + + + + + + + + + + + + + + + + + + + + + + + + + {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} - - + + - - + + - {{ if $val.MaxDrawdown.Highest.Value.IsZero }} - {{else}} - - - - - {{ end }} + {{ end }} + + + + + + + + + + + + + + + + + + + + + + + + + {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} - - + + - - + + + {{end }} + +
Base Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseInitialFunds}} {{$val.FinalHoldings.Pair.Base}}
Quote Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteInitialFunds}} {{$val.FinalHoldings.Pair.Quote}}
Buy Orders{{ $.Prettify.Int $val.BuyOrders}}
Buy Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtValue}} {{$val.FinalHoldings.Pair.Quote}}
Buy Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtAmount}} {{$val.FinalHoldings.Pair.Base}}
Sell Orders{{ $.Prettify.Int $val.SellOrders}}
Sell Value{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldValue}} {{$val.FinalHoldings.Pair.Quote}}
Sell Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldAmount}} {{$val.FinalHoldings.Pair.Base}}
Total Orders{{ $.Prettify.Int $val.TotalOrders}}
Buy Orders{{ $.Prettify.Int $val.BuyOrders}}
Buy Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtValue}} {{$val.FinalHoldings.Pair.Quote}}
Buy Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtAmount}} {{$val.FinalHoldings.Pair.Base}}
Sell Orders{{ $.Prettify.Int $val.SellOrders}}
Sell Value{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldValue}} {{$val.FinalHoldings.Pair.Quote}}Biggest DrawdownStart: {{ $val.MaxDrawdown.Highest.Time }} End: {{ $val.MaxDrawdown.Lowest.Time }} Drop: $ {{ $.Prettify.Decimal8 $val.MaxDrawdown.DrawdownPercent}}%
Starting Close Price{{ $.Prettify.Decimal8 $val.StartingClosePrice.Value}} {{$val.FinalHoldings.Pair.Quote}}
Ending Close Price{{ $.Prettify.Decimal8 $val.EndingClosePrice.Value}} {{ $val.FinalHoldings.Pair.Quote }}
Lowest Close Price{{ $.Prettify.Decimal8 $val.LowestClosePrice.Value}} {{$val.FinalHoldings.Pair.Quote}}
Highest Close Price{{ $.Prettify.Decimal8 $val.HighestClosePrice.Value}} {{ $val.FinalHoldings.Pair.Quote}}
Highest Committed Funds{{ $.Prettify.Decimal8 $val.HighestCommittedFunds.Value}} at {{ $val.HighestCommittedFunds.Time}}
Market Movement{{ $.Prettify.Decimal8 $val.MarketMovement}}%
Sell Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldAmount}} {{$val.FinalHoldings.Pair.Base}}Strategy Movement{{ $.Prettify.Decimal8 $val.StrategyMovement}}%
Total Orders{{ $.Prettify.Int $val.TotalOrders}}Did it beat the market?{{ .DoesPerformanceBeatTheMarket }}
Biggest DrawdownStart: {{ $val.MaxDrawdown.Highest.Time }} End: {{ $val.MaxDrawdown.Lowest.Time }} Drop: $ {{ $.Prettify.Decimal8 $val.MaxDrawdown.DrawdownPercent}}%
Total Value Lost to Volume Sizing{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToVolumeSizing}} {{$val.FinalHoldings.Pair.Quote}}
Total Value Lost to Slippage{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToSlippage}} {{ $val.FinalHoldings.Pair.Quote }}
Total Value Lost{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLost}} {{$val.FinalHoldings.Pair.Quote}}
Total Fees{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalFees}} {{ $val.FinalHoldings.Pair.Quote }}
Final Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteSize}} {{ $val.FinalHoldings.Pair.Quote}}
Final Holdings{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseSize}} {{$val.FinalHoldings.Pair.Base}}
Starting Close Price{{ $.Prettify.Decimal8 $val.StartingClosePrice}} {{$val.FinalHoldings.Pair.Quote}}Final Holdings Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseValue}} {{ $val.FinalHoldings.Pair.Quote }}
Ending Close Price{{ $.Prettify.Decimal8 $val.EndingClosePrice}} {{ $val.FinalHoldings.Pair.Quote }}Total Value{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValue}} {{ $val.FinalHoldings.Pair.Quote}}
+ {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} + Rates + + - - + + - - + + + +
Lowest Close Price{{ $.Prettify.Decimal8 $val.LowestClosePrice}} {{$val.FinalHoldings.Pair.Quote}}Risk Free Rate{{$.Statistics.RiskFreeRate}}%
Highest Close Price{{ $.Prettify.Decimal8 $val.HighestClosePrice}} {{ $val.FinalHoldings.Pair.Quote}}Compound Annual Growth Rate{{ $.Prettify.Decimal8 $val.CompoundAnnualGrowthRate}}%
+ {{if $val.ShowMissingDataWarning}} +

Missing data was detected during this backtesting run
+ Ratio calculations will be skewed

+ {{end}} + Arithmetic Ratios + + - - + + - - + + - {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} - - - - - - - - - {{ end }} - - + + - - + + + +
Highest Committed Funds{{ $.Prettify.Decimal8 $val.HighestCommittedFunds.Value}} at {{ $val.HighestCommittedFunds.Time}}Sharpe Ratio{{$val.ArithmeticRatios.SharpeRatio}}
Market Movement{{ $.Prettify.Decimal8 $val.MarketMovement}}%Sortino Ratio{{$val.ArithmeticRatios.SortinoRatio}}
Strategy Movement{{ $.Prettify.Decimal8 $val.StrategyMovement}}%
Did it beat the market?{{ .DoesPerformanceBeatTheMarket }}
Total Value Lost to Volume Sizing{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToVolumeSizing}} {{$val.FinalHoldings.Pair.Quote}}Information Ratio{{$val.ArithmeticRatios.InformationRatio}}
Total Value Lost to Slippage{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToSlippage}} {{ $val.FinalHoldings.Pair.Quote }}Calmar Ratio{{$val.ArithmeticRatios.CalmarRatio}}
+ Geometric Ratios + + - - + + - - + + - - + + - - + + - {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} - - - - - - - - - {{end }}
Total Value Lost{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLost}} {{$val.FinalHoldings.Pair.Quote}}Sharpe Ratio{{$val.GeometricRatios.SharpeRatio}}
Total Fees{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalFees}} {{ $val.FinalHoldings.Pair.Quote }}Sortino Ratio{{$val.GeometricRatios.SortinoRatio}}
Final Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteSize}} {{ $val.FinalHoldings.Pair.Quote}}Information Ratio{{$val.GeometricRatios.InformationRatio}}
Final Holdings{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseSize}} {{$val.FinalHoldings.Pair.Base}}Calmar Ratio{{$val.GeometricRatios.CalmarRatio}}
Final Holdings Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseValue}} {{ $val.FinalHoldings.Pair.Quote }}
Total Value{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValue}} {{ $val.FinalHoldings.Pair.Quote}}
- {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} - Rates - - - - - - - - - - - -
Risk Free Rate{{$.Statistics.RiskFreeRate}}%
Compound Annual Growth Rate{{ $.Prettify.Decimal8 $val.CompoundAnnualGrowthRate}}%
- {{if $val.ShowMissingDataWarning}} -

Missing data was detected during this backtesting run
- Ratio calculations will be skewed

- {{end}} - Arithmetic Ratios - - - - - - - - - - - - - - - - - - - -
Sharpe Ratio{{$val.ArithmeticRatios.SharpeRatio}}
Sortino Ratio{{$val.ArithmeticRatios.SortinoRatio}}
Information Ratio{{$val.ArithmeticRatios.InformationRatio}}
Calmar Ratio{{$val.ArithmeticRatios.CalmarRatio}}
- Geometric Ratios - - - - - - - - - - - - - - - - - - - -
Sharpe Ratio{{$val.GeometricRatios.SharpeRatio}}
Sortino Ratio{{$val.GeometricRatios.SortinoRatio}}
Information Ratio{{$val.GeometricRatios.InformationRatio}}
Calmar Ratio{{$val.GeometricRatios.CalmarRatio}}
- {{end }} -
+ {{end }}
- {{end}} +
{{end}} {{end}} - {{ if $.Config.StrategySettings.DisableUSDTracking }} - {{ if $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding }} -
-
-

Exchange level funding ratios require USD Tracking setting enabled

-
-
- {{end}} - {{end}} + {{end}} + {{ if $.Config.StrategySettings.DisableUSDTracking }} + {{ if $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding }} +
+
+

Exchange level funding ratios require USD Tracking setting enabled

+
+
+ {{end}} + {{end}} {{ range .Statistics.FundingStatistics.Items }}
@@ -1266,167 +1313,167 @@ {{ if .ReportItem.ShowInfinite}} Infinity% {{else}} - {{ $.Prettify.Decimal8 .ReportItem.Difference}}% + {{ $.Prettify.Decimal8 .ReportItem.Difference}}% {{end}} {{ if eq $.Config.StrategySettings.DisableUSDTracking false }} - Starting Close Price - {{$.Prettify.Decimal8 .StartingClosePrice.Value}} at {{.StartingClosePrice.Time}} + Starting Close Price + {{$.Prettify.Decimal8 .StartingClosePrice.Value}} at {{.StartingClosePrice.Time}} + + + Ending Close Price + {{$.Prettify.Decimal8 .EndingClosePrice.Value}} at {{.EndingClosePrice.Time}} + + + Highest Close Price + {{$.Prettify.Decimal8 .HighestClosePrice.Value}} at {{.HighestClosePrice.Time}} + + + Lowest Close Price + {{$.Prettify.Decimal8 .LowestClosePrice.Value}} at {{.LowestClosePrice.Time}} + + + Market Movement + {{$.Prettify.Decimal8 .MarketMovement}} + + + Strategy Movement + {{ if .ReportItem.ShowInfinite}} + {{$.Prettify.Decimal8 .StrategyMovement}} + {{ else }} + Infinity% + {{end}} + + + Did Strategy Beat The Market? + {{.DidStrategyBeatTheMarket}} + + + Compound Annual Growth Rate + {{$.Prettify.Decimal8 .CompoundAnnualGrowthRate}}% + + {{end}} + + +
+
+ {{end}} + {{ if eq $.Config.StrategySettings.DisableUSDTracking false }} +
+
+

USD Totals Funding Statistics

+
+
+ + + + + - - + + - - + + - - + + - - + + - - {{ if .ReportItem.ShowInfinite}} - - {{ else }} - - {{end}} + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + - {{end}}
Initial Total USD Value${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.InitialHoldingValue.Value}}
Ending Close Price{{$.Prettify.Decimal8 .EndingClosePrice.Value}} at {{.EndingClosePrice.Time}}Final Total USD Value${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.FinalHoldingValue.Value}}
Highest Close Price{{$.Prettify.Decimal8 .HighestClosePrice.Value}} at {{.HighestClosePrice.Time}}Difference{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.HoldingValueDifference}}%
Lowest Close Price{{$.Prettify.Decimal8 .LowestClosePrice.Value}} at {{.LowestClosePrice.Time}}Risk Free Rate{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.RiskFreeRate}}
Market Movement{{$.Prettify.Decimal8 .MarketMovement}}Benchmark movement{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.BenchmarkMarketMovement}}
Strategy Movement{{$.Prettify.Decimal8 .StrategyMovement}}Infinity%Overall strategy movement{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.StrategyMovement}}
Did Strategy Beat The Market?{{.DidStrategyBeatTheMarket}}Did strategy beat the benchmark?{{.Statistics.FundingStatistics.TotalUSDStatistics.DidStrategyBeatTheMarket}}
Compound Annual Growth Rate{{$.Prettify.Decimal8 .CompoundAnnualGrowthRate}}%Did overall strategy make a profit?{{.Statistics.FundingStatistics.TotalUSDStatistics.DidStrategyMakeProfit}}
Highest Holdings${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.HighestHoldingValue.Value}} at {{.Statistics.FundingStatistics.TotalUSDStatistics.HighestHoldingValue.Time}}
Lowest Holdings${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.LowestHoldingValue.Value}} at {{.Statistics.FundingStatistics.TotalUSDStatistics.LowestHoldingValue.Time}}
Total Buy Orders{{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.BuyOrders }}
Total Sell Orders{{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.SellOrders }}
Total Orders{{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.TotalOrders }}
Max DrawdownStart: {{ .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.Highest.Time }} End: {{ .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.Lowest.Time }} Drop: {{ $.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.DrawdownPercent}}%
-
-
- {{end}} - {{ if eq $.Config.StrategySettings.DisableUSDTracking false }} -
-
-

USD Totals Funding Statistics

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Initial Total USD Value${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.InitialHoldingValue.Value}}
Final Total USD Value${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.FinalHoldingValue.Value}}
Difference{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.HoldingValueDifference}}%
Risk Free Rate{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.RiskFreeRate}}
Benchmark movement{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.BenchmarkMarketMovement}}
Overall strategy movement{{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.StrategyMovement}}
Did strategy beat the benchmark?{{.Statistics.FundingStatistics.TotalUSDStatistics.DidStrategyBeatTheMarket}}
Did overall strategy make a profit?{{.Statistics.FundingStatistics.TotalUSDStatistics.DidStrategyMakeProfit}}
Highest Holdings${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.HighestHoldingValue.Value}} at {{.Statistics.FundingStatistics.TotalUSDStatistics.HighestHoldingValue.Time}}
Lowest Holdings${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.LowestHoldingValue.Value}} at {{.Statistics.FundingStatistics.TotalUSDStatistics.LowestHoldingValue.Time}}
Total Buy Orders{{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.BuyOrders }}
Total Sell Orders{{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.SellOrders }}
Total Orders{{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.TotalOrders }}
Max DrawdownStart: {{ .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.Highest.Time }} End: {{ .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.Lowest.Time }} Drop: {{ $.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.DrawdownPercent}}%
- {{if $.Statistics.WasAnyDataMissing}} -

Missing data was detected during this backtesting run
- Ratio calculations will be skewed

- {{end}} - Arithmetic Ratios - - - - - - - - - - - - - - - - - - - -
Sharpe Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.SharpeRatio}}
Sortino Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.SortinoRatio}}
Information Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.InformationRatio}}
Calmar Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.CalmarRatio}}
- Geometric Ratios - - - - - - - - - - - - - - - - - - - -
Sharpe Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.SharpeRatio}}
Sortino Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.SortinoRatio}}
Information Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.InformationRatio}}
Calmar Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.CalmarRatio}}
-
+ {{if $.Statistics.WasAnyDataMissing}} +

Missing data was detected during this backtesting run
+ Ratio calculations will be skewed

+ {{end}} + Arithmetic Ratios + + + + + + + + + + + + + + + + + + + +
Sharpe Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.SharpeRatio}}
Sortino Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.SortinoRatio}}
Information Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.InformationRatio}}
Calmar Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.ArithmeticRatios.CalmarRatio}}
+ Geometric Ratios + + + + + + + + + + + + + + + + + + + +
Sharpe Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.SharpeRatio}}
Sortino Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.SortinoRatio}}
Information Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.InformationRatio}}
Calmar Ratio{{ .Statistics.FundingStatistics.TotalUSDStatistics.GeometricRatios.CalmarRatio}}
+
{{ end }}
@@ -1455,12 +1502,12 @@ {{range $val.FinalOrders.Orders}} - {{ .SpotOrder.Date }} + {{ .Order.Date }} {{ $.Prettify.Decimal8 .ClosePrice}} {{$pair.Quote}} - {{ .SpotOrder.Side }} - {{$.Prettify.Float8 .SpotOrder.Price }} {{$pair.Quote}} - {{$.Prettify.Float8 .SpotOrder.Amount }} {{$pair.Base}} - {{$.Prettify.Float8 .SpotOrder.Fee }} {{$pair.Quote}} + {{ .Order.Side }} + {{$.Prettify.Float8 .Order.Price }} {{$pair.Quote}} + {{$.Prettify.Float8 .Order.Amount }} {{$pair.Base}} + {{$.Prettify.Float8 .Order.Fee }} {{$pair.Quote}} {{ $.Prettify.Decimal8 .CostBasis }} {{$pair.Quote}} {{ $.Prettify.Decimal8 .SlippageRate }}% From 6313ab0d86bd7f1de11d8a68ebb3417e74fd6cdb Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 15 Mar 2022 15:35:32 +1100 Subject: [PATCH 097/171] Appends pnl to receiving curr. Fixes map[time]. accurate USD --- backtester/data/kline/kline.go | 10 +-- backtester/data/kline/kline_types.go | 3 +- backtester/engine/backtest.go | 44 +++++++--- .../eventhandlers/portfolio/portfolio.go | 2 +- .../eventhandlers/portfolio/portfolio_test.go | 4 +- .../portfolio/portfolio_types.go | 5 +- .../statistics/fundingstatistics.go | 28 +++---- backtester/eventhandlers/statistics/pnl.go | 5 ++ backtester/eventtypes/signal/signal.go | 2 +- backtester/funding/funding.go | 84 +++++++++++++++---- backtester/funding/funding_test.go | 16 ++-- backtester/funding/funding_types.go | 15 ++-- backtester/report/tpl.gohtml | 14 +--- engine/datahistory_manager.go | 41 ++++----- engine/datahistory_manager_test.go | 18 ++-- engine/datahistory_manager_types.go | 2 +- engine/rpcserver.go | 4 +- exchanges/exchange.go | 9 +- exchanges/ftx/ftx_wrapper.go | 10 ++- exchanges/order/futures.go | 6 ++ exchanges/order/futures_types.go | 3 +- 21 files changed, 208 insertions(+), 117 deletions(-) create mode 100644 backtester/eventhandlers/statistics/pnl.go diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index c3bfaba186a..382784a52e3 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -22,7 +22,7 @@ func (d *DataFromKline) HasDataAtTime(t time.Time) bool { // Load sets the candle data to the stream for processing func (d *DataFromKline) Load() error { - d.addedTimes = make(map[time.Time]bool) + d.addedTimes = make(map[int64]bool) if len(d.Item.Candles) == 0 { return errNoCandleData } @@ -47,7 +47,7 @@ func (d *DataFromKline) Load() error { ValidationIssues: d.Item.Candles[i].ValidationIssues, } klineData[i] = klinerino - d.addedTimes[d.Item.Candles[i].Time.UTC()] = true + d.addedTimes[d.Item.Candles[i].Time.UTC().Unix()] = true } d.SetStream(klineData) @@ -58,14 +58,14 @@ func (d *DataFromKline) Load() error { // AppendResults adds a candle item to the data stream and sorts it to ensure it is all in order func (d *DataFromKline) AppendResults(ki *gctkline.Item) { if d.addedTimes == nil { - d.addedTimes = make(map[time.Time]bool) + d.addedTimes = make(map[int64]bool) } var klineData []common.DataEventHandler var gctCandles []gctkline.Candle for i := range ki.Candles { - if _, ok := d.addedTimes[ki.Candles[i].Time]; !ok { + if _, ok := d.addedTimes[ki.Candles[i].Time.Unix()]; !ok { gctCandles = append(gctCandles, ki.Candles[i]) - d.addedTimes[ki.Candles[i].Time] = true + d.addedTimes[ki.Candles[i].Time.Unix()] = true } } var candleTimes []time.Time diff --git a/backtester/data/kline/kline_types.go b/backtester/data/kline/kline_types.go index d44d395f51c..0c466ace3a0 100644 --- a/backtester/data/kline/kline_types.go +++ b/backtester/data/kline/kline_types.go @@ -2,7 +2,6 @@ package kline import ( "errors" - "time" "github.com/thrasher-corp/gocryptotrader/backtester/data" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" @@ -14,7 +13,7 @@ var errNoCandleData = errors.New("no candle data provided") // It holds candle data for a specified range with helper functions type DataFromKline struct { data.Base - addedTimes map[time.Time]bool + addedTimes map[int64]bool Item gctkline.Item RangeHolder *gctkline.IntervalRangeHolder } diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 6056cdc8861..9a36d1ed76e 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -17,6 +17,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/currency" + gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -100,8 +103,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { return err } - if ev.GetAssetType().IsFutures() { - // hardcoded fix + if bt.Funding.HasFutures() { err = bt.Funding.UpdateCollateral(ev) if err != nil { return err @@ -112,18 +114,12 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { case common.DataEventHandler: if bt.Strategy.UsingSimultaneousProcessing() { err = bt.processSimultaneousDataEvents() - if err != nil { - return err - } - bt.Funding.CreateSnapshot(ev.GetTime()) - return nil + } else { + err = bt.processSingleDataEvent(eType, funds.FundReleaser()) } - err = bt.processSingleDataEvent(eType, funds.FundReleaser()) if err != nil { return err } - bt.Funding.CreateSnapshot(ev.GetTime()) - return nil case signal.Event: bt.processSignalEvent(eType, funds.FundReserver()) case order.Event: @@ -135,6 +131,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { errUnhandledDatatype, ev) } + bt.Funding.CreateSnapshot(ev.GetTime()) return nil } @@ -247,6 +244,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if err != nil { return err } + return bt.Statistic.AddPNLForTime(pnl) } @@ -299,7 +297,6 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Errorf(common.SubLoggers[common.Backtester], "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } - err = bt.Statistic.SetEventForOffset(t) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) @@ -348,6 +345,30 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Errorf(common.SubLoggers[common.Backtester], "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } + + var exch gctexchange.IBotExchange + exch, err = bt.exchangeManager.GetExchangeByName(ev.GetExchange()) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + + var receivingCurrency currency.Code + var receivingAsset asset.Item + receivingCurrency, receivingAsset, err = exch.GetCurrencyForRealisedPNL(ev.GetAssetType(), ev.Pair()) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + rPNL := pnl.GetRealisedPNL() + if !rPNL.PNL.IsZero() { + err = bt.Funding.RealisePNL(ev.GetExchange(), receivingAsset, receivingCurrency, rPNL.PNL) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "RealisePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } + } + err = bt.Statistic.AddPNLForTime(pnl) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) @@ -358,6 +379,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) log.Errorf(common.SubLoggers[common.Backtester], "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return } + } } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 3121e72da60..d425b258217 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -493,7 +493,7 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat // that are not closed and calculate their PNL func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) error { if !e.GetAssetType().IsFutures() { - return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFutureAsset) + return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFuturesAsset) } settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index ddf2c80436a..2e28f303f18 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -712,8 +712,8 @@ func TestCalculatePNL(t *testing.T) { p := &Portfolio{} ev := &kline.Kline{} err := p.UpdatePNL(ev, decimal.Zero) - if !errors.Is(err, gctorder.ErrNotFutureAsset) { - t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFutureAsset) + if !errors.Is(err, gctorder.ErrNotFuturesAsset) { + t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFuturesAsset) } exch := &ftx.FTX{} diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index b2af0373bc3..d26fcfa619d 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -92,12 +92,15 @@ type PNLSummary struct { Result gctorder.PNLResult } -// IPNL +// IPNL defines an interface for an implementation +// to retrieve PNL from a position type IPNL interface { GetUnrealisedPNL() BasicPNLResult GetRealisedPNL() BasicPNLResult } +// BasicPNLResult holds the time and the pnl +// of a position type BasicPNLResult struct { Time time.Time PNL decimal.Decimal diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index e7c0ef92a9a..dd080746aa0 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -63,20 +63,20 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str usdStats.BuyOrders += response.Items[i].BuyOrders usdStats.SellOrders += response.Items[i].SellOrders } - for k, v := range report.USDTotalsOverTime { - if usdStats.HighestHoldingValue.Value.LessThan(v.USDValue) { - usdStats.HighestHoldingValue.Time = k - usdStats.HighestHoldingValue.Value = v.USDValue + for i := range report.USDTotalsOverTime { + if usdStats.HighestHoldingValue.Value.LessThan(report.USDTotalsOverTime[i].USDValue) { + usdStats.HighestHoldingValue.Time = report.USDTotalsOverTime[i].Time + usdStats.HighestHoldingValue.Value = report.USDTotalsOverTime[i].USDValue } if usdStats.LowestHoldingValue.Value.IsZero() { - usdStats.LowestHoldingValue.Time = k - usdStats.LowestHoldingValue.Value = v.USDValue + usdStats.LowestHoldingValue.Time = report.USDTotalsOverTime[i].Time + usdStats.LowestHoldingValue.Value = report.USDTotalsOverTime[i].USDValue } - if usdStats.LowestHoldingValue.Value.GreaterThan(v.USDValue) && !usdStats.LowestHoldingValue.Value.IsZero() { - usdStats.LowestHoldingValue.Time = k - usdStats.LowestHoldingValue.Value = v.USDValue + if usdStats.LowestHoldingValue.Value.GreaterThan(report.USDTotalsOverTime[i].USDValue) && !usdStats.LowestHoldingValue.Value.IsZero() { + usdStats.LowestHoldingValue.Time = report.USDTotalsOverTime[i].Time + usdStats.LowestHoldingValue.Value = report.USDTotalsOverTime[i].USDValue } - usdStats.HoldingValues = append(usdStats.HoldingValues, ValueAtTime{Time: k, Value: v.USDValue}) + usdStats.HoldingValues = append(usdStats.HoldingValues, ValueAtTime{Time: report.USDTotalsOverTime[i].Time, Value: report.USDTotalsOverTime[i].USDValue}) } sort.Slice(usdStats.HoldingValues, func(i, j int) bool { return usdStats.HoldingValues[i].Time.Before(usdStats.HoldingValues[j].Time) @@ -155,11 +155,8 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f if disableUSDTracking { return item, nil } + closePrices := reportItem.Snapshots - item.IsCollateral = reportItem.IsCollateral - if item.IsCollateral { - return item, nil - } if len(closePrices) == 0 { return nil, errMissingSnapshots } @@ -225,6 +222,7 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f } } } + item.TotalOrders = item.BuyOrders + item.SellOrders if !item.ReportItem.ShowInfinite && !reportItem.IsCollateral { if item.ReportItem.Snapshots[0].USDValue.IsZero() { @@ -255,7 +253,7 @@ func CalculateIndividualFundingStatistics(disableUSDTracking bool, reportItem *f } } } - if item.ReportItem.USDPairCandle == nil { + if item.ReportItem.USDPairCandle == nil && !reportItem.IsCollateral { return nil, fmt.Errorf("%w usd candles missing", errMissingSnapshots) } s := item.ReportItem.USDPairCandle.GetStream() diff --git a/backtester/eventhandlers/statistics/pnl.go b/backtester/eventhandlers/statistics/pnl.go new file mode 100644 index 00000000000..76e6d7b2e7f --- /dev/null +++ b/backtester/eventhandlers/statistics/pnl.go @@ -0,0 +1,5 @@ +package statistics + +func CalculatePNLStatistics() { + +} diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index 76aa1a36968..4fe8b49f016 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -69,7 +69,7 @@ func (s *Signal) SetAmount(d decimal.Decimal) { // GetUnderlyingPair returns the underlaying currency pair func (s *Signal) GetUnderlyingPair() (currency.Pair, error) { if !s.AssetType.IsFutures() { - return s.CurrencyPair, order.ErrNotFutureAsset + return s.CurrencyPair, order.ErrNotFuturesAsset } return s.UnderlyingPair, nil } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index e90f24cabd5..851b26e65ce 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -18,6 +18,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" ) var ( @@ -115,11 +116,18 @@ func (f *FundManager) CreateSnapshot(t time.Time) { for i := range f.items { if f.items[i].snapshot == nil { f.items[i].snapshot = make(map[int64]ItemSnapshot) + if f.items[i].isCollateral { + f.items[i].initialFunds = f.items[i].available + } + } else if _, ok := f.items[i].snapshot[t.Unix()]; ok { + f.items[i].snapshot[t.Unix()] = ItemSnapshot{} } + iss := ItemSnapshot{ Available: f.items[i].available, Time: t, } + if !f.disableUSDTracking { var usdClosePrice decimal.Decimal if f.items[i].trackingCandles == nil { @@ -275,7 +283,6 @@ func (f *FundManager) USDTrackingDisabled() bool { // GenerateReport builds report data for result HTML report func (f *FundManager) GenerateReport() *Report { report := Report{ - USDTotalsOverTime: make(map[time.Time]ItemSnapshot), UsingExchangeLevelFunding: f.usingExchangeLevelFunding, DisableUSDTracking: f.disableUSDTracking, } @@ -290,6 +297,7 @@ func (f *FundManager) GenerateReport() *Report { FinalFunds: f.items[i].available, IsCollateral: f.items[i].isCollateral, } + if !f.disableUSDTracking && f.items[i].trackingCandles != nil { usdStream := f.items[i].trackingCandles.GetStream() @@ -301,17 +309,35 @@ func (f *FundManager) GenerateReport() *Report { } var pricingOverTime []ItemSnapshot + snaps: for _, v := range f.items[i].snapshot { - if !f.items[i].isCollateral { - pricingOverTime = append(pricingOverTime, v) - if !f.disableUSDTracking { - usdTotalForPeriod := report.USDTotalsOverTime[v.Time] - usdTotalForPeriod.Time = v.Time - usdTotalForPeriod.USDValue = usdTotalForPeriod.USDValue.Add(v.USDValue) - report.USDTotalsOverTime[v.Time] = usdTotalForPeriod + pricingOverTime = append(pricingOverTime, v) + if !f.items[i].asset.IsFutures() && !f.disableUSDTracking { + for j := range report.USDTotalsOverTime { + if report.USDTotalsOverTime[j].Time.Equal(v.Time) { + report.USDTotalsOverTime[j].USDValue = report.USDTotalsOverTime[j].USDValue.Add(v.USDValue) + report.USDTotalsOverTime[j].Breakdown = append(report.USDTotalsOverTime[j].Breakdown, Thing{ + Currency: f.items[i].currency, + USD: v.USDValue, + }) + continue snaps + } else { + continue + } } + report.USDTotalsOverTime = append(report.USDTotalsOverTime, ItemSnapshot{ + Time: v.Time, + USDValue: v.USDValue, + Breakdown: []Thing{ + { + Currency: f.items[i].currency, + USD: v.USDValue, + }, + }, + }) } } + sort.Slice(pricingOverTime, func(i, j int) bool { return pricingOverTime[i].Time.Before(pricingOverTime[j].Time) }) @@ -328,6 +354,11 @@ func (f *FundManager) GenerateReport() *Report { items = append(items, item) } + + sort.Slice(report.USDTotalsOverTime, func(i, j int) bool { + return report.USDTotalsOverTime[i].Time.Before(report.USDTotalsOverTime[j].Time) + }) + report.Items = items return &report } @@ -413,11 +444,11 @@ func (f *FundManager) IsUsingExchangeLevelFunding() bool { // GetFundingForEvent This will construct a funding based on a backtesting event func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, error) { - return f.GetFundingForEAP(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) + return f.getFundingForEAP(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) } // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair -func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { +func (f *FundManager) getFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { var resp Pair var collat Collateral if a.IsFutures() { @@ -452,7 +483,7 @@ func (f *FundManager) GetFundingForEAP(exch string, a asset.Item, p currency.Pai } // GetFundingForEAC This will construct a funding based on the exchange, asset, currency code -func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Code) (*Item, error) { +func (f *FundManager) getFundingForEAC(exch string, a asset.Item, c currency.Code) (*Item, error) { for i := range f.items { if f.items[i].BasicEqual(exch, a, c, currency.EMPTYCODE) { return f.items[i], nil @@ -515,7 +546,7 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { exchMap := make(map[string]exchange.IBotExchange) var collateralAmount decimal.Decimal var err error - butts := gctorder.TotalCollateralCalculator{ + calculator := gctorder.TotalCollateralCalculator{ CalculateOffline: true, } @@ -547,7 +578,7 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { side = gctorder.Sell } - butts.CollateralAssets = append(butts.CollateralAssets, gctorder.CollateralCalculator{ + calculator.CollateralAssets = append(calculator.CollateralAssets, gctorder.CollateralCalculator{ CalculateOffline: true, CollateralCurrency: f.items[i].currency, Asset: f.items[i].asset, @@ -557,13 +588,12 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { USDPrice: usd, }) } - futureCurrency, futureAsset, err := exchMap[ev.GetExchange()].GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) if err != nil { return err } - collat, err := exchMap[ev.GetExchange()].CalculateTotalCollateral(context.TODO(), &butts) + collat, err := exchMap[ev.GetExchange()].CalculateTotalCollateral(context.TODO(), &calculator) if err != nil { return err } @@ -578,3 +608,27 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { } return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, ev.GetExchange(), ev.GetAssetType(), futureCurrency) } + +func (f *FundManager) HasFutures() bool { + for i := range f.items { + if f.items[i].isCollateral || f.items[i].asset.IsFutures() { + return true + } + } + return false +} + +// RealisePNL adds the realised PNL to a receiving exchange asset pair +func (f *FundManager) RealisePNL(receivingExchange string, receivingAsset asset.Item, receivingCurrency currency.Code, realisedPNL decimal.Decimal) error { + log.Debugf(log.Global, "\n%v %v %v %v WOW\n", receivingExchange, receivingAsset, receivingCurrency, realisedPNL) + for i := range f.items { + if f.items[i].exchange == receivingExchange && + f.items[i].asset == receivingAsset && + f.items[i].currency.Equal(receivingCurrency) { + f.items[i].available = f.items[i].available.Add(realisedPNL) + return nil + + } + } + return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, realisedPNL, receivingExchange, receivingAsset, receivingCurrency) +} diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 01b0ac5a1ed..f17715cc51d 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -199,12 +199,12 @@ func TestExists(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.GetFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.GetFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -244,7 +244,7 @@ func TestExists(t *testing.T) { t.Errorf("received '%v' expected '%v'", exists, true) } - currFunds, err := f.GetFundingForEAC(exch, a, base) + currFunds, err := f.getFundingForEAC(exch, a, base) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -272,7 +272,7 @@ func TestAddPair(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.GetFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -334,7 +334,7 @@ func TestGetFundingForEvent(t *testing.T) { func TestGetFundingForEAC(t *testing.T) { t.Parallel() f := FundManager{} - _, err := f.GetFundingForEAC(exch, a, base) + _, err := f.getFundingForEAC(exch, a, base) if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } @@ -347,7 +347,7 @@ func TestGetFundingForEAC(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - fundo, err := f.GetFundingForEAC(exch, a, base) + fundo, err := f.getFundingForEAC(exch, a, base) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -360,7 +360,7 @@ func TestGetFundingForEAC(t *testing.T) { func TestGetFundingForEAP(t *testing.T) { t.Parallel() f := FundManager{} - _, err := f.GetFundingForEAP(exch, a, pair) + _, err := f.getFundingForEAP(exch, a, pair) if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } @@ -380,7 +380,7 @@ func TestGetFundingForEAP(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.GetFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exch, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 7c71f848e7b..ec10a7ca8ff 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -25,9 +25,7 @@ type FundManager struct { type IFundingManager interface { Reset() IsUsingExchangeLevelFunding() bool - GetFundingForEAC(string, asset.Item, currency.Code) (*Item, error) GetFundingForEvent(common.EventHandler) (IFundingPair, error) - GetFundingForEAP(string, asset.Item, currency.Pair) (IFundingPair, error) Transfer(decimal.Decimal, *Item, *Item, bool) error GenerateReport() *Report AddUSDTrackingData(*kline.DataFromKline) error @@ -36,6 +34,8 @@ type IFundingManager interface { LiquidateByCollateral(currency.Code) error GetAllFunding() []BasicItem UpdateCollateral(common.EventHandler) error + HasFutures() bool + RealisePNL(receivingExchange string, receivingAsset asset.Item, receivingCurrency currency.Code, realisedPNL decimal.Decimal) error } // IFundingReader is a simple interface of @@ -43,7 +43,6 @@ type IFundingManager interface { // manager type IFundingReader interface { GetFundingForEvent(common.EventHandler) (IFundingPair, error) - GetFundingForEAP(string, asset.Item, currency.Pair) (IFundingPair, error) GetAllFunding() []BasicItem } @@ -67,9 +66,7 @@ type IFundReader interface { type IFundTransferer interface { IsUsingExchangeLevelFunding() bool Transfer(decimal.Decimal, *Item, *Item, bool) error - GetFundingForEAC(string, asset.Item, currency.Code) (*Item, error) GetFundingForEvent(common.EventHandler) (IFundingPair, error) - GetFundingForEAP(string, asset.Item, currency.Pair) (IFundingPair, error) } // IFundReserver limits funding usage for portfolio event handling @@ -169,7 +166,7 @@ type Report struct { DisableUSDTracking bool UsingExchangeLevelFunding bool Items []ReportItem - USDTotalsOverTime map[time.Time]ItemSnapshot + USDTotalsOverTime []ItemSnapshot } // ReportItem holds reporting fields @@ -199,4 +196,10 @@ type ItemSnapshot struct { Available decimal.Decimal USDClosePrice decimal.Decimal USDValue decimal.Decimal + Breakdown []Thing +} + +type Thing struct { + Currency currency.Code + USD decimal.Decimal } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 5e5aecb9956..9293d170066 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -258,12 +258,8 @@ Exchange Asset Currency - Initial Funds - Final Funds - {{ if eq .Config.StrategySettings.DisableUSDTracking false }} - Initial Fund in USD - Final Funds in USD - {{end}} + Initial Collateral + Final Collateral Difference @@ -274,12 +270,8 @@ {{.Exchange}} {{.Asset}} {{.Currency}} - {{ $.Prettify.Decimal8 .InitialFunds}} {{.Currency}} + {{ $.Prettify.Decimal8 .InitialFunds }} {{.Currency}} {{ $.Prettify.Decimal8 .FinalFunds }} {{.Currency}} - {{ if eq $.Config.StrategySettings.DisableUSDTracking false }} - ${{ $.Prettify.Decimal2 .USDInitialFunds}} - ${{ $.Prettify.Decimal2 .USDFinalFunds}} - {{ end }} {{if .ShowInfinite}} Infinity% {{ else }} diff --git a/engine/datahistory_manager.go b/engine/datahistory_manager.go index 5a831826d93..f9580ead26b 100644 --- a/engine/datahistory_manager.go +++ b/engine/datahistory_manager.go @@ -192,7 +192,7 @@ func (m *DataHistoryManager) compareJobsToData(jobs ...*DataHistoryJob) error { jobs[i].rangeHolder.SetHasDataFromCandles(candles.Candles) case dataHistoryTradeDataType: for x := range jobs[i].rangeHolder.Ranges { - results, ok := jobs[i].Results[jobs[i].rangeHolder.Ranges[x].Start.Time] + results, ok := jobs[i].Results[jobs[i].rangeHolder.Ranges[x].Start.Time.Unix()] if !ok { continue } @@ -358,7 +358,7 @@ ranges: } } if skipProcessing { - _, ok := job.Results[job.rangeHolder.Ranges[i].Start.Time] + _, ok := job.Results[job.rangeHolder.Ranges[i].Start.Time.Unix()] if !ok && !job.OverwriteExistingData { // we have determined that data is there, however it is not reflected in // this specific job's results, which is required for a job to be complete @@ -367,7 +367,7 @@ ranges: if err != nil { return err } - job.Results[job.rangeHolder.Ranges[i].Start.Time] = []DataHistoryJobResult{ + job.Results[job.rangeHolder.Ranges[i].Start.Time.Unix()] = []DataHistoryJobResult{ { ID: id, JobID: job.ID, @@ -397,7 +397,7 @@ ranges: var failures int64 hasDataInRange := false - resultLookup, ok := job.Results[job.rangeHolder.Ranges[i].Start.Time] + resultLookup, ok := job.Results[job.rangeHolder.Ranges[i].Start.Time.Unix()] if ok { for x := range resultLookup { switch resultLookup[x].Status { @@ -419,7 +419,7 @@ ranges: for x := range resultLookup { resultLookup[x].Status = dataHistoryIntervalIssuesFound } - job.Results[job.rangeHolder.Ranges[i].Start.Time] = resultLookup + job.Results[job.rangeHolder.Ranges[i].Start.Time.Unix()] = resultLookup continue } } @@ -451,16 +451,16 @@ ranges: return errNilResult } - lookup := job.Results[result.IntervalStartDate] + lookup := job.Results[result.IntervalStartDate.Unix()] lookup = append(lookup, *result) - job.Results[result.IntervalStartDate] = lookup + job.Results[result.IntervalStartDate.Unix()] = lookup } completed := true // nolint:ifshort,nolintlint // false positive and triggers only on Windows allResultsSuccessful := true allResultsFailed := true completionCheck: for i := range job.rangeHolder.Ranges { - result, ok := job.Results[job.rangeHolder.Ranges[i].Start.Time] + result, ok := job.Results[job.rangeHolder.Ranges[i].Start.Time.Unix()] if !ok { completed = false } @@ -503,21 +503,22 @@ func (m *DataHistoryManager) runValidationJob(job *DataHistoryJob, exch exchange nextIntervalToProcess := job.StartDate timesToFetch: for t, results := range job.Results { + tt := time.Unix(t, 0) if len(results) < int(job.MaxRetryAttempts) { for x := range results { if results[x].Status == dataHistoryStatusComplete { continue timesToFetch } } - intervalsToCheck = append(intervalsToCheck, t) + intervalsToCheck = append(intervalsToCheck, tt) } else { for x := range results { results[x].Status = dataHistoryIntervalIssuesFound } job.Results[t] = results } - if t.After(nextIntervalToProcess) { - nextIntervalToProcess = t.Add(intervalLength) + if tt.After(nextIntervalToProcess) { + nextIntervalToProcess = tt.Add(intervalLength) } } for i := nextIntervalToProcess; i.Before(job.EndDate); i = i.Add(intervalLength) { @@ -548,9 +549,9 @@ timesToFetch: if err != nil { return err } - lookup := job.Results[result.IntervalStartDate] + lookup := job.Results[result.IntervalStartDate.Unix()] lookup = append(lookup, *result) - job.Results[result.IntervalStartDate] = lookup + job.Results[result.IntervalStartDate.Unix()] = lookup } completed := true // nolint:ifshort,nolintlint // false positive and triggers only on Windows @@ -558,7 +559,7 @@ timesToFetch: allResultsFailed := true completionCheck: for i := range jobIntervals { - results, ok := job.Results[jobIntervals[i]] + results, ok := job.Results[jobIntervals[i].Unix()] if !ok { completed = false break @@ -1198,7 +1199,7 @@ func (m *DataHistoryManager) validateJob(job *DataHistoryJob) error { return fmt.Errorf("job %s exchange %s asset %s currency %s %w", job.Nickname, job.Exchange, job.Asset, job.Pair, errCurrencyNotEnabled) } if job.Results == nil { - job.Results = make(map[time.Time][]DataHistoryJobResult) + job.Results = make(map[int64][]DataHistoryJobResult) } if job.RunBatchLimit <= 0 { log.Warnf(log.DataHistory, "job %s has unset batch limit, defaulting to %v", job.Nickname, defaultDataHistoryBatchLimit) @@ -1514,11 +1515,11 @@ func (m *DataHistoryManager) convertDBModelToJob(dbModel *datahistoryjob.DataHis return resp, nil } -func (m *DataHistoryManager) convertDBResultToJobResult(dbModels []*datahistoryjobresult.DataHistoryJobResult) (map[time.Time][]DataHistoryJobResult, error) { +func (m *DataHistoryManager) convertDBResultToJobResult(dbModels []*datahistoryjobresult.DataHistoryJobResult) (map[int64][]DataHistoryJobResult, error) { if !m.IsRunning() { return nil, ErrSubSystemNotStarted } - result := make(map[time.Time][]DataHistoryJobResult) + result := make(map[int64][]DataHistoryJobResult) for i := range dbModels { id, err := uuid.FromString(dbModels[i].ID) if err != nil { @@ -1529,7 +1530,7 @@ func (m *DataHistoryManager) convertDBResultToJobResult(dbModels []*datahistoryj if err != nil { return nil, err } - lookup := result[dbModels[i].IntervalStartDate] + lookup := result[dbModels[i].IntervalStartDate.Unix()] lookup = append(lookup, DataHistoryJobResult{ ID: id, JobID: jobID, @@ -1539,13 +1540,13 @@ func (m *DataHistoryManager) convertDBResultToJobResult(dbModels []*datahistoryj Result: dbModels[i].Result, Date: dbModels[i].Date, }) - result[dbModels[i].IntervalStartDate] = lookup + result[dbModels[i].IntervalStartDate.Unix()] = lookup } return result, nil } -func (m *DataHistoryManager) convertJobResultToDBResult(results map[time.Time][]DataHistoryJobResult) []*datahistoryjobresult.DataHistoryJobResult { +func (m *DataHistoryManager) convertJobResultToDBResult(results map[int64][]DataHistoryJobResult) []*datahistoryjobresult.DataHistoryJobResult { var response []*datahistoryjobresult.DataHistoryJobResult for _, v := range results { for i := range v { diff --git a/engine/datahistory_manager_test.go b/engine/datahistory_manager_test.go index 348ce0f3436..a128a364b92 100644 --- a/engine/datahistory_manager_test.go +++ b/engine/datahistory_manager_test.go @@ -893,8 +893,8 @@ func TestConverters(t *testing.T) { Result: "test123", Date: time.Now(), } - mapperino := make(map[time.Time][]DataHistoryJobResult) - mapperino[dhj.StartDate] = append(mapperino[dhj.StartDate], jr) + mapperino := make(map[int64][]DataHistoryJobResult) + mapperino[dhj.StartDate.Unix()] = append(mapperino[dhj.StartDate.Unix()], jr) result := m.convertJobResultToDBResult(mapperino) if jr.ID.String() != result[0].ID || jr.JobID.String() != result[0].JobID || @@ -910,13 +910,13 @@ func TestConverters(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } - if jr.ID != andBackAgain[dhj.StartDate][0].ID || - jr.JobID != andBackAgain[dhj.StartDate][0].JobID || - jr.Result != andBackAgain[dhj.StartDate][0].Result || - !jr.Date.Equal(andBackAgain[dhj.StartDate][0].Date) || - !jr.IntervalStartDate.Equal(andBackAgain[dhj.StartDate][0].IntervalStartDate) || - !jr.IntervalEndDate.Equal(andBackAgain[dhj.StartDate][0].IntervalEndDate) || - jr.Status != andBackAgain[dhj.StartDate][0].Status { + if jr.ID != andBackAgain[dhj.StartDate.Unix()][0].ID || + jr.JobID != andBackAgain[dhj.StartDate.Unix()][0].JobID || + jr.Result != andBackAgain[dhj.StartDate.Unix()][0].Result || + !jr.Date.Equal(andBackAgain[dhj.StartDate.Unix()][0].Date) || + !jr.IntervalStartDate.Equal(andBackAgain[dhj.StartDate.Unix()][0].IntervalStartDate) || + !jr.IntervalEndDate.Equal(andBackAgain[dhj.StartDate.Unix()][0].IntervalEndDate) || + jr.Status != andBackAgain[dhj.StartDate.Unix()][0].Status { t.Error("expected matching job") } } diff --git a/engine/datahistory_manager_types.go b/engine/datahistory_manager_types.go index 0401d71891b..5e9b69851ca 100644 --- a/engine/datahistory_manager_types.go +++ b/engine/datahistory_manager_types.go @@ -155,7 +155,7 @@ type DataHistoryJob struct { MaxRetryAttempts int64 Status dataHistoryStatus CreatedDate time.Time - Results map[time.Time][]DataHistoryJobResult + Results map[int64][]DataHistoryJobResult rangeHolder *kline.IntervalRangeHolder OverwriteExistingData bool ConversionInterval kline.Interval diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 22788a1671a..b19e98f138d 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -3349,10 +3349,10 @@ func (s *RPCServer) FindMissingSavedTradeIntervals(_ context.Context, r *gctrpc. start = start.Truncate(time.Hour) end = end.Truncate(time.Hour) - intervalMap := make(map[time.Time]bool) + intervalMap := make(map[int64]bool) iterationTime := start for iterationTime.Before(end) { - intervalMap[iterationTime] = false + intervalMap[iterationTime.Unix()] = false iterationTime = iterationTime.Add(time.Hour) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 0fe348dda66..642d4bc30dd 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1415,7 +1415,7 @@ func (b *Base) GetAvailableTransferChains(_ context.Context, _ currency.Code) ([ // CalculatePNL is an overridable function to allow PNL to be calculated on an // open position // It will also determine whether the position is considered to be liquidated -// For live trading, an overrided function may wish to confirm the liquidation by +// For live trading, an overriding function may wish to confirm the liquidation by // requesting the status of the asset func (b *Base) CalculatePNL(context.Context, *order.PNLCalculatorRequest) (*order.PNLResult, error) { return nil, common.ErrNotYetImplemented @@ -1442,3 +1442,10 @@ func (b *Base) GetFuturesPositions(context.Context, asset.Item, currency.Pair, t func (b *Base) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, asset.Item, error) { return currency.Code{}, "", common.ErrNotYetImplemented } + +// GetCurrencyForRealisedPNL returns where to put realised PNL +// example 1: FTX PNL is paid out in USD to your spot wallet +// example 2: Binance coin margined futures pays returns using the same currency eg BTC +func (b *Base) GetCurrencyForRealisedPNL(_ asset.Item, _ currency.Pair) (currency.Code, asset.Item, error) { + return currency.Code{}, "", common.ErrNotYetImplemented +} diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 8f2df05907c..5f5429f66f6 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1672,9 +1672,11 @@ func (f *FTX) GetFuturesPositions(ctx context.Context, a asset.Item, cp currency } // GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair -func (f *FTX) GetCollateralCurrencyForContract(i asset.Item, cp currency.Pair) (currency.Code, asset.Item, error) { - if !i.IsFutures() { - return currency.Code{}, "", fmt.Errorf("%v %w", i, order.ErrNotFuturesAsset) - } +func (f *FTX) GetCollateralCurrencyForContract(_ asset.Item, _ currency.Pair) (currency.Code, asset.Item, error) { return currency.USD, asset.Futures, nil } + +// GetCurrencyForRealisedPNL returns where to put realised PNL +func (f *FTX) GetCurrencyForRealisedPNL(_ asset.Item, _ currency.Pair) (currency.Code, asset.Item, error) { + return currency.USD, asset.Spot, nil +} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 2175de17f29..be24c6e3d34 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -611,6 +611,12 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { return nil } +// GetCurrencyForRealisedPNL is a generic handling of determining the asset +// to assign realised PNL into, which is just itself +func (p *PNLCalculator) GetCurrencyForRealisedPNL(realisedAsset asset.Item, realisedPair currency.Pair) (currency.Code, asset.Item, error) { + return realisedPair.Base, realisedAsset, nil +} + // CalculatePNL this is a localised generic way of calculating open // positions' worth, it is an implementation of the PNLCalculation interface func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorRequest) (*PNLResult, error) { diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 040d10d79fa..15e6c575aa8 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -32,8 +32,6 @@ var ( ErrUSDValueRequired = errors.New("USD value required") // ErrOfflineCalculationSet is raised when collateral calculation is set to be offline, yet is attempted online ErrOfflineCalculationSet = errors.New("offline calculation set") - // ErrNotFutureAsset - ErrNotFutureAsset = errors.New("not a futures asset") errExchangeNameEmpty = errors.New("exchange name empty") errTimeUnset = errors.New("time unset") @@ -52,6 +50,7 @@ var ( // ways of calculating PNL to be used for futures positions type PNLCalculation interface { CalculatePNL(context.Context, *PNLCalculatorRequest) (*PNLResult, error) + GetCurrencyForRealisedPNL(realisedAsset asset.Item, realisedPair currency.Pair) (currency.Code, asset.Item, error) } // CollateralManagement is an interface that allows From 365b09a9fd71645e91c10f5ddd64ff8d78aff09d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 16 Mar 2022 15:53:40 +1100 Subject: [PATCH 098/171] Improves report output rendering --- .../eventhandlers/portfolio/portfolio.go | 16 + .../portfolio/portfolio_types.go | 13 +- .../eventhandlers/statistics/statistics.go | 1 + backtester/report/tpl.gohtml | 382 +++++++++++------- exchanges/order/futures.go | 1 + exchanges/order/futures_types.go | 1 + 6 files changed, 266 insertions(+), 148 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index d425b258217..a0ed6dda18f 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -585,6 +585,7 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er return response, nil } response.Result = pnlHistory[len(pnlHistory)-1] + response.CollateralCurrency = positions[0].Underlying return response, nil } @@ -655,3 +656,18 @@ func (p PNLSummary) GetRealisedPNL() BasicPNLResult { PNL: p.Result.RealisedPNL, } } + +// GetExposure returns the position exposure +func (p PNLSummary) GetExposure() decimal.Decimal { + return p.Result.Exposure +} + +// GetCollateralCurrency returns the collateral currency +func (p PNLSummary) GetCollateralCurrency() currency.Code { + return p.CollateralCurrency +} + +// GetDirection returns the direction +func (p PNLSummary) GetDirection() gctorder.Side { + return p.Result.Direction +} diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index d26fcfa619d..e72f2620395 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -85,11 +85,12 @@ type Settings struct { // PNLSummary holds a PNL result along with // exchange details type PNLSummary struct { - Exchange string - Item asset.Item - Pair currency.Pair - Offset int64 - Result gctorder.PNLResult + Exchange string + Item asset.Item + Pair currency.Pair + CollateralCurrency currency.Code + Offset int64 + Result gctorder.PNLResult } // IPNL defines an interface for an implementation @@ -97,6 +98,8 @@ type PNLSummary struct { type IPNL interface { GetUnrealisedPNL() BasicPNLResult GetRealisedPNL() BasicPNLResult + GetCollateralCurrency() currency.Code + GetDirection() gctorder.Side } // BasicPNLResult holds the time and the pnl diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index f1c2f3cc0b7..c8e685a88d6 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -140,6 +140,7 @@ func (s *Statistic) AddPNLForTime(pnl *portfolio.PNLSummary) error { for i := len(lookup.Events) - 1; i >= 0; i-- { if lookup.Events[i].DataEvent.GetOffset() == pnl.Offset { lookup.Events[i].PNL = pnl + lookup.Events[i].Holdings.BaseSize = pnl.Result.Exposure return nil } } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 9293d170066..5acf7aa4759 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -388,17 +388,15 @@
+ + + + + - - - - - - - @@ -415,8 +413,7 @@ - - + @@ -436,8 +433,7 @@ - - + @@ -469,7 +465,9 @@ + + @@ -479,8 +477,10 @@ - + + + {{end}} @@ -496,9 +496,6 @@
Strategy nameIs multi currencyCustom settings
Strategy name {{.Config.StrategySettings.Name}}
Is multi currency {{.Config.StrategySettings.SimultaneousSignalProcessing}}
Custom settings {{.Config.StrategySettings.CustomSettings}}
Exchange Name AssetCurrency BaseCurrency QuoteCurrency Buy side Min Amount Buy side Max Amount Buy side Max Total
{{.ExchangeName}} {{.Asset}}{{.Base}}{{.Quote}}{{.Base}}-{{.Quote}} {{ $.Prettify.Decimal64 .BuySide.MinimumSize}} {{.Base}} {{ $.Prettify.Decimal64 .BuySide.MaximumSize}} {{.Base}} {{ $.Prettify.Decimal64 .BuySide.MaximumTotal}} {{.Quote}}Currency Paired With Initial FundsFinal Funds Transfer FeeIs Collateral
{{.Asset}} {{.Currency}} {{.PairedWith}}{{ $.Prettify.Decimal8 .InitialFunds}}{{ $.Prettify.Decimal8 .InitialFunds}} {{.Currency}}{{ $.Prettify.Decimal8 .FinalFunds}} {{.Currency}} {{ $.Prettify.Decimal64 .TransferFee}}{{ .IsCollateral }}
- - - @@ -509,9 +506,6 @@ - - - @@ -1059,6 +1053,14 @@ + + + + + + + + @@ -1095,39 +1097,49 @@
Can Use LeverageMax Leverage RateMax Orders With Leverage Ratio Buy side Min Amount Buy side Max Amount Buy side Max Total
{{.Config.PortfolioSettings.Leverage.CanUseLeverage}}{{.Config.PortfolioSettings.Leverage.MaximumOrderLeverageRate}}{{.Config.PortfolioSettings.Leverage.MaximumOrdersWithLeverageRatio}} {{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MinimumSize}} {{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumSize}} {{ $.Prettify.Decimal64 .Config.PortfolioSettings.BuySide.MaximumTotal}}Total Sell Orders {{$.Prettify.Int .Statistics.TotalSellOrders}}
Total Long Orders{{ $.Prettify.Int .Statistics.TotalLongOrders}}
Total Short Orders{{ $.Prettify.Int .Statistics.TotalShortOrders}}
Total Orders {{$.Prettify.Int .Statistics.TotalOrders}}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + {{ if $val.Asset.IsFutures }} + + + + + + + + + {{ else }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{end}} @@ -1155,58 +1167,96 @@ - - - - + {{ if $val.Asset.IsFutures }} + {{else}} + + + + + {{end}} + - {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} + {{ if $val.Asset.IsFutures }} + {{else}} + {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} + + + + + + + + + {{ end }} - - + + - - + + + + + + - {{ end }} - - - - - - - - - - - - - - - - - - - - - - - - - {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} - - + + - - + + - {{end }} + {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} + + + + + + + + + {{else}} + + + + + + + + + + + + + + + + + + + + + + + + + {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} + + + + + + + + + {{end }} + {{end}} + {{end}}
Base Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseInitialFunds}} {{$val.FinalHoldings.Pair.Base}}
Quote Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteInitialFunds}} {{$val.FinalHoldings.Pair.Quote}}
Buy Orders{{ $.Prettify.Int $val.BuyOrders}}
Buy Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtValue}} {{$val.FinalHoldings.Pair.Quote}}
Buy Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtAmount}} {{$val.FinalHoldings.Pair.Base}}
Sell Orders{{ $.Prettify.Int $val.SellOrders}}
Sell Value{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldValue}} {{$val.FinalHoldings.Pair.Quote}}
Sell Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldAmount}} {{$val.FinalHoldings.Pair.Base}}
Long Orders{{ $.Prettify.Int $val.LongOrders}}
Short Orders{{ $.Prettify.Int $val.ShortOrders}}
Base Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseInitialFunds}} {{$val.FinalHoldings.Pair.Base}}
Quote Initial Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteInitialFunds}} {{$val.FinalHoldings.Pair.Quote}}
Buy Orders{{ $.Prettify.Int $val.BuyOrders}}
Buy Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtValue}} {{$val.FinalHoldings.Pair.Quote}}
Buy Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtAmount}} {{$val.FinalHoldings.Pair.Base}}
Sell Orders{{ $.Prettify.Int $val.SellOrders}}
Sell Value{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldValue}} {{$val.FinalHoldings.Pair.Quote}}
Sell Amount{{ $.Prettify.Decimal8 $val.FinalHoldings.SoldAmount}} {{$val.FinalHoldings.Pair.Base}}
Total Orders {{ $.Prettify.Int $val.TotalOrders}}Highest Close Price {{ $.Prettify.Decimal8 $val.HighestClosePrice.Value}} {{ $val.FinalHoldings.Pair.Quote}}
Highest Committed Funds{{ $.Prettify.Decimal8 $val.HighestCommittedFunds.Value}} at {{ $val.HighestCommittedFunds.Time}}
Highest Committed Funds{{ $.Prettify.Decimal8 $val.HighestCommittedFunds.Value}} at {{ $val.HighestCommittedFunds.Time}}
Market Movement {{ $.Prettify.Decimal8 $val.MarketMovement}}%
Strategy Movement{{ $.Prettify.Decimal8 $val.StrategyMovement}}%
Did it beat the market?{{ .DoesPerformanceBeatTheMarket }}
Strategy Movement{{ $.Prettify.Decimal8 $val.StrategyMovement}}%Total Value Lost to Volume Sizing{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToVolumeSizing}}
Did it beat the market?{{ .DoesPerformanceBeatTheMarket }}Total Value Lost to Slippage{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToSlippage}}
Total Value Lost{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLost}}
Total Value Lost to Volume Sizing{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToVolumeSizing}} {{$val.FinalHoldings.Pair.Quote}}
Total Value Lost to Slippage{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToSlippage}} {{ $val.FinalHoldings.Pair.Quote }}
Total Value Lost{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLost}} {{$val.FinalHoldings.Pair.Quote}}
Total Fees{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalFees}} {{ $val.FinalHoldings.Pair.Quote }}
Final Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteSize}} {{ $val.FinalHoldings.Pair.Quote}}
Final Holdings{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseSize}} {{$val.FinalHoldings.Pair.Base}}
Final Holdings Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseValue}} {{ $val.FinalHoldings.Pair.Quote }}Total Fees{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalFees}}
Total Value{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValue}} {{ $val.FinalHoldings.Pair.Quote}}Final Holdings{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseSize}} {{$val.FinalHoldings.Pair.Base}}-{{ $val.FinalHoldings.Pair.Quote }}
Final Holdings Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseValue}} {{ $val.FinalHoldings.Pair.Quote }}
Total Value{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValue}} {{ $val.FinalHoldings.Pair.Quote}}
Total Value Lost to Volume Sizing{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToVolumeSizing}} {{$val.FinalHoldings.Pair.Quote}}
Total Value Lost to Slippage{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToSlippage}} {{ $val.FinalHoldings.Pair.Quote }}
Total Value Lost{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLost}} {{$val.FinalHoldings.Pair.Quote}}
Total Fees{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalFees}} {{ $val.FinalHoldings.Pair.Quote }}
Final Funds{{ $.Prettify.Decimal8 $val.FinalHoldings.QuoteSize}} {{ $val.FinalHoldings.Pair.Quote}}
Final Holdings{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseSize}} {{$val.FinalHoldings.Pair.Base}}
Final Holdings Value{{ $.Prettify.Decimal8 $val.FinalHoldings.BaseValue}} {{ $val.FinalHoldings.Pair.Quote }}
Total Value{{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValue}} {{ $val.FinalHoldings.Pair.Quote}}
{{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} @@ -1292,14 +1342,26 @@
- - - - - - - - + {{ if .ReportItem.IsCollateral}} + + + + + + + + + {{ else }} + + + + + + + + + {{end }} + {{ if .ReportItem.ShowInfinite}} @@ -1309,42 +1371,46 @@ {{end}} {{ if eq $.Config.StrategySettings.DisableUSDTracking false }} - - - - - - - - - - - - - - - - - - - - - - - {{ if .ReportItem.ShowInfinite}} - - {{ else }} - - {{end}} - - - - - - - - - + {{ if .ReportItem.IsCollateral}} + {{ else }} + + + + + + + + + + + + + + + + + + + + + + + {{ if .ReportItem.ShowInfinite}} + + {{ else }} + + {{end}} + + + + + + + + + + {{end }} + {{end}}
Initial Funds{{ $.Prettify.Decimal8 .ReportItem.InitialFunds}}
Final Funds{{ $.Prettify.Decimal8 .ReportItem.FinalFunds}}
Initial Collateral{{ $.Prettify.Decimal8 .ReportItem.InitialFunds}}
Final Collateral{{ $.Prettify.Decimal8 .ReportItem.FinalFunds}}
Initial Funds{{ $.Prettify.Decimal8 .ReportItem.InitialFunds}}
Final Funds{{ $.Prettify.Decimal8 .ReportItem.FinalFunds}}
Difference
Starting Close Price{{$.Prettify.Decimal8 .StartingClosePrice.Value}} at {{.StartingClosePrice.Time}}
Ending Close Price{{$.Prettify.Decimal8 .EndingClosePrice.Value}} at {{.EndingClosePrice.Time}}
Highest Close Price{{$.Prettify.Decimal8 .HighestClosePrice.Value}} at {{.HighestClosePrice.Time}}
Lowest Close Price{{$.Prettify.Decimal8 .LowestClosePrice.Value}} at {{.LowestClosePrice.Time}}
Market Movement{{$.Prettify.Decimal8 .MarketMovement}}
Strategy Movement{{$.Prettify.Decimal8 .StrategyMovement}}Infinity%
Did Strategy Beat The Market?{{.DidStrategyBeatTheMarket}}
Compound Annual Growth Rate{{$.Prettify.Decimal8 .CompoundAnnualGrowthRate}}%
Starting Close Price{{$.Prettify.Decimal8 .StartingClosePrice.Value}} at {{.StartingClosePrice.Time}}
Ending Close Price{{$.Prettify.Decimal8 .EndingClosePrice.Value}} at {{.EndingClosePrice.Time}}
Highest Close Price{{$.Prettify.Decimal8 .HighestClosePrice.Value}} at {{.HighestClosePrice.Time}}
Lowest Close Price{{$.Prettify.Decimal8 .LowestClosePrice.Value}} at {{.LowestClosePrice.Time}}
Market Movement{{$.Prettify.Decimal8 .MarketMovement}}
Strategy Movement{{$.Prettify.Decimal8 .StrategyMovement}}Infinity%
Did Strategy Beat The Market?{{.DidStrategyBeatTheMarket}}
Compound Annual Growth Rate{{$.Prettify.Decimal8 .CompoundAnnualGrowthRate}}%
@@ -1407,6 +1473,14 @@ Total Sell Orders {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.SellOrders }} + + Total Long Orders + {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.LongOrders }} + + + Total Short Orders + {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.ShortOrders }} + Total Orders {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.TotalOrders }} @@ -1531,27 +1605,49 @@ Price Action Why - {{$pair.Base}} Funds - {{$pair.Quote}} Funds - Total Value + {{if $asset.IsFutures}} + Holdings + Direction + Unrealised PNL + Realised PNL + {{ else }} + {{$pair.Base}} Funds + {{$pair.Quote}} Funds + Total Value + {{ end }} + {{range $ev := $data.Events}} {{ if ne $ev.FillEvent nil }} {{$ev.FillEvent.GetTime}} - {{$.Prettify.Decimal8 $ev.FillEvent.GetClosePrice}} {{$pair.Quote}} + {{ $.Prettify.Decimal8 $ev.FillEvent.GetClosePrice}} {{if $asset.IsFutures}}{{$ev.PNL.GetCollateralCurrency}}{{else}}{{$pair.Quote}}{{end}} {{$ev.FillEvent.GetDirection}} {{$ev.FillEvent.GetReason}} {{ else if ne $ev.SignalEvent nil}} {{$ev.SignalEvent.GetTime}} - {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{$pair.Quote}} + {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{if $asset.IsFutures}}{{$ev.PNL.GetCollateralCurrency}}{{else}}{{$pair.Quote}}{{end}} {{$ev.SignalEvent.GetDirection}} {{$ev.SignalEvent.GetReason}} {{ end }} - {{ $.Prettify.Decimal8 $ev.Holdings.BaseSize}} {{$pair.Base}} - {{ $.Prettify.Decimal8 $ev.Holdings.QuoteSize}} {{$pair.Quote}} - {{ $.Prettify.Decimal8 $ev.Holdings.TotalValue}} {{$pair.Quote}} + {{if $asset.IsFutures}} + {{if ne $ev.PNL nil }} + {{ $.Prettify.Decimal8 $ev.PNL.GetExposure}} {{$pair.Base}}-{{$pair.Quote}} + {{$ev.PNL.GetDirection}} + {{$ev.PNL.GetUnrealisedPNL.PNL}} + {{$ev.PNL.GetRealisedPNL.PNL}} + {{else}} + 0 {{$pair.Base}}-{{$pair.Quote}} + N/A + 0 + 0 + {{end}} + {{else }} + {{ $.Prettify.Decimal8 $ev.Holdings.BaseSize}} {{$pair.Base}} + {{ $.Prettify.Decimal8 $ev.Holdings.QuoteSize}} {{$pair.Quote}} + {{ $.Prettify.Decimal8 $ev.Holdings.TotalValue}} {{$pair.Quote}} + {{end}} {{end}} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index be24c6e3d34..21ba8f89995 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -673,6 +673,7 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque Price: calc.CurrentPrice, Exposure: currentExposure, Fee: calc.Fee, + Direction: calc.CurrentDirection, } return response, nil } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 15e6c575aa8..622fff6e5bf 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -265,6 +265,7 @@ type PNLResult struct { RealisedPNL decimal.Decimal Price decimal.Decimal Exposure decimal.Decimal + Direction Side Fee decimal.Decimal IsLiquidated bool } From f0b1c95efc9d4e9403f6feacd504c9c9f24ca7ea Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 17 Mar 2022 12:56:39 +1100 Subject: [PATCH 099/171] PNL stats in report. New tests for futures --- .../eventhandlers/portfolio/portfolio.go | 13 +- .../portfolio/portfolio_types.go | 5 +- backtester/eventhandlers/portfolio/setup.go | 9 +- .../statistics/currencystatistics.go | 12 +- .../ftxcashandcarry/ftxcashandcarry.go | 9 +- backtester/report/tpl.gohtml | 32 +++-- exchanges/order/futures.go | 80 ++++++++--- exchanges/order/futures_test.go | 128 ++++++++++++++++++ exchanges/order/futures_types.go | 45 +++--- 9 files changed, 270 insertions(+), 63 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index a0ed6dda18f..62c2538cb6e 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -585,7 +585,7 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er return response, nil } response.Result = pnlHistory[len(pnlHistory)-1] - response.CollateralCurrency = positions[0].Underlying + response.CollateralCurrency = positions[0].CollateralCurrency return response, nil } @@ -631,6 +631,7 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { pnlHistory := positions[len(positions)-1].PNLHistory if len(pnlHistory) > 0 { summary.Result = pnlHistory[len(pnlHistory)-1] + summary.CollateralCurrency = positions[0].CollateralCurrency } } @@ -644,16 +645,18 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { // GetUnrealisedPNL returns a basic struct containing unrealised PNL func (p PNLSummary) GetUnrealisedPNL() BasicPNLResult { return BasicPNLResult{ - Time: p.Result.Time, - PNL: p.Result.UnrealisedPNL, + Time: p.Result.Time, + PNL: p.Result.UnrealisedPNL, + Currency: p.CollateralCurrency, } } // GetRealisedPNL returns a basic struct containing realised PNL func (p PNLSummary) GetRealisedPNL() BasicPNLResult { return BasicPNLResult{ - Time: p.Result.Time, - PNL: p.Result.RealisedPNL, + Time: p.Result.Time, + PNL: p.Result.RealisedPNL, + Currency: p.CollateralCurrency, } } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index e72f2620395..76d5edb8cac 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -105,6 +105,7 @@ type IPNL interface { // BasicPNLResult holds the time and the pnl // of a position type BasicPNLResult struct { - Time time.Time - PNL decimal.Decimal + Currency currency.Code + Time time.Time + PNL decimal.Decimal } diff --git a/backtester/eventhandlers/portfolio/setup.go b/backtester/eventhandlers/portfolio/setup.go index 3bd7a00043d..a4f42bdf5fa 100644 --- a/backtester/eventhandlers/portfolio/setup.go +++ b/backtester/eventhandlers/portfolio/setup.go @@ -63,7 +63,10 @@ func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { if _, ok := p.exchangeAssetPairSettings[name][setup.Asset][setup.Pair]; ok { return nil } - + collateralCurrency, _, err := setup.Exchange.GetCollateralCurrencyForContract(setup.Asset, setup.Pair) + if err != nil { + return err + } settings := &Settings{ Fee: setup.ExchangeFee, BuySideSizing: setup.BuySide, @@ -82,11 +85,13 @@ func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { Underlying: setup.Pair.Base, OfflineCalculation: true, UseExchangePNLCalculation: setup.UseExchangePNLCalculation, + CollateralCurrency: collateralCurrency, } if setup.UseExchangePNLCalculation { futureTrackerSetup.ExchangePNLCalculation = setup.Exchange } - tracker, err := gctorder.SetupMultiPositionTracker(futureTrackerSetup) + var tracker *gctorder.MultiPositionTracker + tracker, err = gctorder.SetupMultiPositionTracker(futureTrackerSetup) if err != nil { return err } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index ad35d5c2903..c20665e8477 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -151,25 +151,25 @@ func (c *CurrencyPairStatistic) analysePNLGrowth() { var unrealised, realised portfolio.BasicPNLResult unrealised = c.Events[i].PNL.GetUnrealisedPNL() realised = c.Events[i].PNL.GetRealisedPNL() - if unrealised.PNL.LessThan(lowestUnrealised.Value) || (!unrealised.PNL.IsZero() && !lowestUnrealised.Set) { + if unrealised.PNL.LessThan(lowestUnrealised.Value) || !lowestUnrealised.Set { lowestUnrealised.Value = unrealised.PNL lowestUnrealised.Time = unrealised.Time lowestUnrealised.Set = true } - if unrealised.PNL.GreaterThan(highestUnrealised.Value) || (!unrealised.PNL.IsZero() && !highestUnrealised.Set) { + if unrealised.PNL.GreaterThan(highestUnrealised.Value) || !highestUnrealised.Set { highestUnrealised.Value = unrealised.PNL highestUnrealised.Time = unrealised.Time highestUnrealised.Set = true } - if realised.PNL.LessThan(lowestRealised.Value) || (!realised.PNL.IsZero() && !lowestRealised.Set) { + if realised.PNL.LessThan(lowestRealised.Value) || !lowestRealised.Set { lowestRealised.Value = realised.PNL lowestRealised.Time = realised.Time lowestRealised.Set = true } - if realised.PNL.GreaterThan(highestRealised.Value) || (!realised.PNL.IsZero() && !highestRealised.Set) { - lowestRealised.Value = realised.PNL - lowestRealised.Time = realised.Time + if realised.PNL.GreaterThan(highestRealised.Value) || !highestRealised.Set { + highestRealised.Value = realised.PNL + highestRealised.Time = realised.Time highestRealised.Set = true } } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 1e07df53586..b983dc08cf8 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -79,15 +79,16 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf case len(pos) == 0: // check to see if order is appropriate to action spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) - spotSignal.AppendReason(fmt.Sprintf("signalling purchase of %v", spotSignal.Pair())) + spotSignal.AppendReason(fmt.Sprintf("Signalling purchase of %v", spotSignal.Pair())) // first the spot purchase spotSignal.SetDirection(order.Buy) // second the futures purchase, using the newly acquired asset // as collateral to short futuresSignal.SetDirection(order.Short) futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) + futuresSignal.AppendReason("Shorting to perform cash and carry") futuresSignal.CollateralCurrency = spotSignal.CurrencyPair.Base - spotSignal.AppendReason(fmt.Sprintf("signalling shorting %v", futuresSignal.Pair())) + spotSignal.AppendReason(fmt.Sprintf("Signalling shorting of %v", futuresSignal.Pair())) // set the FillDependentEvent to use the futures signal // as the futures signal relies on a completed spot order purchase // to use as collateral @@ -96,12 +97,12 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf response = append(response, &spotSignal) case len(pos) > 0 && v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReason("closing position on last event") + futuresSignal.AppendReason("Closing position on last event") response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReason("closing position after reaching close short distance percentage") + futuresSignal.AppendReason("Closing position after reaching close short distance percentage") response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed && diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 5acf7aa4759..b4993755aa6 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1106,6 +1106,22 @@ Short Orders {{ $.Prettify.Int $val.ShortOrders}} + + Lowest Unrealised PNL + {{ $.Prettify.Decimal8 $val.LowestUnrealisedPNL.Value}} at {{ $val.LowestUnrealisedPNL.Time}} + + + Highest Unrealised PNL + {{ $.Prettify.Decimal8 $val.HighestUnrealisedPNL.Value}} at {{ $val.HighestUnrealisedPNL.Time}} + + + Lowest Realised PNL + {{ $.Prettify.Decimal8 $val.LowestRealisedPNL.Value}} at {{ $val.LowestRealisedPNL.Time}} + + + Highest Realised PNL + {{ $.Prettify.Decimal8 $val.HighestRealisedPNL.Value}} at {{ $val.HighestRealisedPNL.Time}} + {{ else }} Base Initial Funds @@ -1607,7 +1623,7 @@ Why {{if $asset.IsFutures}} Holdings - Direction + Position Direction Unrealised PNL Realised PNL {{ else }} @@ -1627,22 +1643,22 @@ {{$ev.FillEvent.GetReason}} {{ else if ne $ev.SignalEvent nil}} {{$ev.SignalEvent.GetTime}} - {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{if $asset.IsFutures}}{{$ev.PNL.GetCollateralCurrency}}{{else}}{{$pair.Quote}}{{end}} + {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{if $asset.IsFutures}}{{$ev.PNL.GetCollateralCurrency}}{{else}}{{$pair.Quote}}{{end}} {{$ev.SignalEvent.GetDirection}} {{$ev.SignalEvent.GetReason}} {{ end }} {{if $asset.IsFutures}} {{if ne $ev.PNL nil }} - {{ $.Prettify.Decimal8 $ev.PNL.GetExposure}} {{$pair.Base}}-{{$pair.Quote}} - {{$ev.PNL.GetDirection}} - {{$ev.PNL.GetUnrealisedPNL.PNL}} - {{$ev.PNL.GetRealisedPNL.PNL}} - {{else}} + {{ $.Prettify.Decimal8 $ev.PNL.GetExposure}} {{$pair.Base}}-{{$pair.Quote}} + {{$ev.PNL.GetDirection}} + {{$ev.PNL.GetUnrealisedPNL.PNL}} {{$ev.PNL.GetCollateralCurrency}} + {{$ev.PNL.GetRealisedPNL.PNL}} {{$ev.PNL.GetCollateralCurrency}} + {{else}} 0 {{$pair.Base}}-{{$pair.Quote}} N/A 0 0 - {{end}} + {{end}} {{else }} {{ $.Prettify.Decimal8 $ev.Holdings.BaseSize}} {{$pair.Base}} {{ $.Prettify.Decimal8 $ev.Holdings.QuoteSize}} {{$pair.Quote}} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 21ba8f89995..3f9bb7910cf 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -64,6 +64,44 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { return multiPositionTracker.TrackNewOrder(d) } +// SetCollateralCurrency allows the setting of a collateral currency to all child trackers +// when using position controller for futures orders tracking +func (c *PositionController) SetCollateralCurrency(exch string, item asset.Item, pair currency.Pair, collateralCurrency currency.Code) error { + if c == nil { + return fmt.Errorf("position controller %w", common.ErrNilPointer) + } + c.m.Lock() + defer c.m.Unlock() + if !item.IsFutures() { + return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) + } + + exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + if !ok { + return fmt.Errorf("cannot set collateral %v for %v %v %v %w", collateralCurrency, exch, item, pair, ErrPositionsNotLoadedForExchange) + } + itemM, ok := exchM[item] + if !ok { + return fmt.Errorf("cannot set collateral %v for %v %v %v %w", collateralCurrency, exch, item, pair, ErrPositionsNotLoadedForAsset) + } + multiPositionTracker, ok := itemM[pair] + if !ok { + return fmt.Errorf("cannot set collateral %v for %v %v %v %w", collateralCurrency, exch, item, pair, ErrPositionsNotLoadedForPair) + } + if multiPositionTracker == nil { + return fmt.Errorf("cannot set collateral %v for %v %v %v %w", collateralCurrency, exch, item, pair, common.ErrNilPointer) + } + multiPositionTracker.m.Lock() + multiPositionTracker.collateralCurrency = collateralCurrency + for i := range multiPositionTracker.positions { + multiPositionTracker.positions[i].m.Lock() + multiPositionTracker.positions[i].collateralCurrency = collateralCurrency + multiPositionTracker.positions[i].m.Unlock() + } + multiPositionTracker.m.Unlock() + return nil +} + // GetPositionsForExchange returns all positions for an // exchange, asset pair that is stored in the position controller func (c *PositionController) GetPositionsForExchange(exch string, item asset.Item, pair currency.Pair) ([]PositionStats, error) { @@ -189,6 +227,7 @@ func (c *PositionController) ClearPositionsForExchange(exch string, item asset.I OfflineCalculation: multiPositionTracker.offlinePNLCalculation, UseExchangePNLCalculation: multiPositionTracker.useExchangePNLCalculations, ExchangePNLCalculation: multiPositionTracker.exchangePNLCalculation, + CollateralCurrency: multiPositionTracker.collateralCurrency, }) if err != nil { return err @@ -226,6 +265,7 @@ func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPosition orderPositions: make(map[string]*PositionTracker), useExchangePNLCalculations: setup.UseExchangePNLCalculation, exchangePNLCalculation: setup.ExchangePNLCalculation, + collateralCurrency: setup.CollateralCurrency, }, nil } @@ -284,6 +324,7 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { Asset: d.AssetType, Side: d.Side, UseExchangePNLCalculation: e.useExchangePNLCalculations, + CollateralCurrency: e.collateralCurrency, } tracker, err := e.SetupPositionTracker(setup) if err != nil { @@ -327,6 +368,7 @@ func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) currentDirection: setup.Side, openingDirection: setup.Side, useExchangePNLCalculation: setup.UseExchangePNLCalculation, + collateralCurrency: setup.CollateralCurrency, offlinePNLCalculation: e.offlinePNLCalculation, } if !setup.UseExchangePNLCalculation { @@ -353,20 +395,21 @@ func (p *PositionTracker) GetStats() PositionStats { orders = append(orders, p.shortPositions...) return PositionStats{ - Exchange: p.exchange, - Asset: p.asset, - Pair: p.contractPair, - Underlying: p.underlyingAsset, - Status: p.status, - Orders: orders, - RealisedPNL: p.realisedPNL, - UnrealisedPNL: p.unrealisedPNL, - LatestDirection: p.currentDirection, - OpeningDirection: p.openingDirection, - OpeningPrice: p.entryPrice, - LatestPrice: p.latestPrice, - PNLHistory: p.pnlHistory, - Exposure: p.exposure, + Exchange: p.exchange, + Asset: p.asset, + Pair: p.contractPair, + Underlying: p.underlyingAsset, + CollateralCurrency: p.collateralCurrency, + Status: p.status, + Orders: orders, + RealisedPNL: p.realisedPNL, + UnrealisedPNL: p.unrealisedPNL, + LatestDirection: p.currentDirection, + OpeningDirection: p.openingDirection, + OpeningPrice: p.entryPrice, + LatestPrice: p.latestPrice, + PNLHistory: p.pnlHistory, + Exposure: p.exposure, } } @@ -394,8 +437,12 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro result.UnrealisedPNL = p.exposure.Mul(diff) } if len(p.pnlHistory) > 0 { - result.RealisedPNLBeforeFees = p.pnlHistory[len(p.pnlHistory)-1].RealisedPNLBeforeFees - result.Exposure = p.pnlHistory[len(p.pnlHistory)-1].Exposure + latest := p.pnlHistory[len(p.pnlHistory)-1] + result.RealisedPNLBeforeFees = latest.RealisedPNLBeforeFees + result.Exposure = latest.Exposure + result.Direction = latest.Direction + result.RealisedPNL = latest.RealisedPNL + result.IsLiquidated = latest.IsLiquidated } var err error p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) @@ -600,6 +647,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { p.unrealisedPNL = decimal.Zero p.pnlHistory[len(p.pnlHistory)-1].RealisedPNL = p.realisedPNL p.pnlHistory[len(p.pnlHistory)-1].UnrealisedPNL = p.unrealisedPNL + p.pnlHistory[len(p.pnlHistory)-1].Direction = p.currentDirection } else if p.exposure.IsNegative() { if p.currentDirection.IsLong() { p.currentDirection = Short diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 274e403bd70..5dd36e69d1c 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -28,6 +28,14 @@ func (f *FakePNL) CalculatePNL(context.Context, *PNLCalculatorRequest) (*PNLResu return f.result, nil } +// GetCurrencyForRealisedPNL overrides default pnl calculations +func (f *FakePNL) GetCurrencyForRealisedPNL(realisedAsset asset.Item, realisedPair currency.Pair) (currency.Code, asset.Item, error) { + if f.err != nil { + return realisedPair.Base, "", f.err + } + return realisedPair.Base, realisedAsset, nil +} + func TestUpsertPNLEntry(t *testing.T) { t.Parallel() var results []PNLResult @@ -802,3 +810,123 @@ func TestUpdateOpenPositionUnrealisedPNL(t *testing.T) { t.Errorf("received '%v' expected '%v", err, common.ErrNilPointer) } } + +func TestSetCollateralCurrency(t *testing.T) { + t.Parallel() + var expectedError = ErrNotFuturesAsset + pc := SetupPositionController() + err := pc.SetCollateralCurrency("hi", asset.Spot, currency.Pair{}, currency.Code{}) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + cp := currency.NewPair(currency.BTC, currency.USDT) + pc.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) + expectedError = ErrPositionsNotLoadedForExchange + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + pc.positionTrackerControllers["hi"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) + expectedError = ErrPositionsNotLoadedForAsset + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + + pc.positionTrackerControllers["hi"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) + expectedError = ErrPositionsNotLoadedForPair + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + + pc.positionTrackerControllers["hi"][asset.Futures][cp] = nil + err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) + expectedError = common.ErrNilPointer + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + + pc.positionTrackerControllers["hi"][asset.Futures][cp] = &MultiPositionTracker{ + exchange: "hi", + asset: asset.Futures, + pair: cp, + orderPositions: make(map[string]*PositionTracker), + } + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: cp, + AssetType: asset.Futures, + Side: Long, + ID: "lol", + Price: 1, + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Fatalf("received '%v' expected '%v", err, nil) + } + + err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) + expectedError = nil + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + + if !pc.positionTrackerControllers["hi"][asset.Futures][cp].collateralCurrency.Equal(currency.DOGE) { + t.Errorf("received '%v' expected '%v'", pc.positionTrackerControllers["hi"][asset.Futures][cp].collateralCurrency, currency.DOGE) + } + + if !pc.positionTrackerControllers["hi"][asset.Futures][cp].positions[0].collateralCurrency.Equal(currency.DOGE) { + t.Errorf("received '%v' expected '%v'", pc.positionTrackerControllers["hi"][asset.Futures][cp].positions[0].collateralCurrency, currency.DOGE) + } + + pc = nil + err = pc.SetCollateralCurrency("hi", asset.Spot, currency.Pair{}, currency.Code{}) + expectedError = common.ErrNilPointer + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } +} + +func TestMPTUpdateOpenPositionUnrealisedPNL(t *testing.T) { + t.Parallel() + var err, expectedError error + expectedError = nil + cp := currency.NewPair(currency.BTC, currency.USDT) + pc := SetupPositionController() + err = pc.TrackNewOrder(&Detail{ + Date: time.Now(), + Exchange: "hi", + Pair: cp, + AssetType: asset.Futures, + Side: Long, + ID: "lol", + Price: 1, + Amount: 1, + }) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + result, err := pc.positionTrackerControllers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + if result.Equal(decimal.NewFromInt(1337)) { + t.Error("") + } + + expectedError = ErrPositionClosed + pc.positionTrackerControllers["hi"][asset.Futures][cp].positions[0].status = Closed + _, err = pc.positionTrackerControllers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } + + expectedError = ErrPositionsNotLoadedForPair + pc.positionTrackerControllers["hi"][asset.Futures][cp].positions = nil + _, err = pc.positionTrackerControllers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v", err, expectedError) + } +} diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 622fff6e5bf..5a258467953 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -136,12 +136,13 @@ type PositionController struct { // is closed, then the position controller will create a new one // to track the current positions type MultiPositionTracker struct { - m sync.Mutex - exchange string - asset asset.Item - pair currency.Pair - underlying currency.Code - positions []*PositionTracker + m sync.Mutex + exchange string + asset asset.Item + pair currency.Pair + underlying currency.Code + collateralCurrency currency.Code + positions []*PositionTracker // order positions allows for an easier time knowing which order is // part of which position tracker orderPositions map[string]*PositionTracker @@ -157,6 +158,7 @@ type MultiPositionTrackerSetup struct { Asset asset.Item Pair currency.Pair Underlying currency.Code + CollateralCurrency currency.Code OfflineCalculation bool UseExchangePNLCalculation bool ExchangePNLCalculation PNLCalculation @@ -175,6 +177,7 @@ type PositionTracker struct { asset asset.Item contractPair currency.Pair underlyingAsset currency.Code + collateralCurrency currency.Code exposure decimal.Decimal currentDirection Side openingDirection Side @@ -198,6 +201,7 @@ type PositionTrackerSetup struct { Pair currency.Pair EntryPrice decimal.Decimal Underlying currency.Code + CollateralCurrency currency.Code Asset asset.Item Side Side UseExchangePNLCalculation bool @@ -273,18 +277,19 @@ type PNLResult struct { // PositionStats is a basic holder // for position information type PositionStats struct { - Exchange string - Asset asset.Item - Pair currency.Pair - Underlying currency.Code - Orders []Detail - RealisedPNL decimal.Decimal - UnrealisedPNL decimal.Decimal - Exposure decimal.Decimal - LatestDirection Side - Status Status - OpeningDirection Side - OpeningPrice decimal.Decimal - LatestPrice decimal.Decimal - PNLHistory []PNLResult + Exchange string + Asset asset.Item + Pair currency.Pair + Underlying currency.Code + CollateralCurrency currency.Code + Orders []Detail + RealisedPNL decimal.Decimal + UnrealisedPNL decimal.Decimal + Exposure decimal.Decimal + LatestDirection Side + Status Status + OpeningDirection Side + OpeningPrice decimal.Decimal + LatestPrice decimal.Decimal + PNLHistory []PNLResult } From 2a55f3d3df040d3118ef1f7bf12853fc7d168ba3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 17 Mar 2022 15:41:35 +1100 Subject: [PATCH 100/171] Fixes existing tests before adding new coverage --- .../config/examples/ftx-cash-carry.strat | 2 +- backtester/eventhandlers/portfolio/helpers.go | 1 - .../eventhandlers/portfolio/portfolio.go | 6 ++--- .../eventhandlers/portfolio/portfolio_test.go | 24 ++++++++++++------- .../eventhandlers/portfolio/size/size.go | 3 +++ .../eventhandlers/portfolio/size/size_test.go | 5 ++++ .../statistics/currencystatistics_test.go | 8 +++---- backtester/report/report_test.go | 4 ++-- 8 files changed, 33 insertions(+), 20 deletions(-) delete mode 100644 backtester/eventhandlers/portfolio/helpers.go diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 04e582baf85..5b47e4a9eff 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -10,7 +10,7 @@ "exchange-name": "ftx", "asset": "spot", "currency": "USD", - "initial-funds": "100", + "initial-funds": "100000", "transfer-fee": "0" } ], diff --git a/backtester/eventhandlers/portfolio/helpers.go b/backtester/eventhandlers/portfolio/helpers.go deleted file mode 100644 index 68cd9cc499a..00000000000 --- a/backtester/eventhandlers/portfolio/helpers.go +++ /dev/null @@ -1 +0,0 @@ -package portfolio diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 62c2538cb6e..b313cd5a9b3 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -54,6 +54,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi }, Direction: ev.GetDirection(), FillDependentEvent: ev.GetFillDependentEvent(), + Amount: ev.GetAmount(), } if ev.GetDirection() == "" { return o, errInvalidDirection @@ -158,11 +159,10 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, siz if err != nil { originalOrderSignal.AppendReason(err.Error()) switch d.GetDirection() { - case gctorder.Buy: + case gctorder.Buy, common.CouldNotBuy: originalOrderSignal.Direction = common.CouldNotBuy - case gctorder.Sell: + case gctorder.Sell, common.CouldNotSell: originalOrderSignal.Direction = common.CouldNotSell - case common.CouldNotBuy, common.CouldNotSell: case gctorder.Short: originalOrderSignal.Direction = common.CouldNotShort case gctorder.Long: diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 2e28f303f18..46032b923ee 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -273,12 +273,13 @@ func TestUpdate(t *testing.T) { t.Fatal(err) } pair, err := funding.CreatePair(b, q) - if err != nil { - t.Fatal(err) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) } + err = p.UpdateHoldings(&kline.Kline{}, pair) - if !errors.Is(err, errNoPortfolioSettings) { - t.Errorf("received '%v' expected '%v'", err, errNoPortfolioSettings) + if !errors.Is(err, errExchangeUnset) { + t.Errorf("received '%v' expected '%v'", err, errExchangeUnset) } tt := time.Now() @@ -561,6 +562,7 @@ func TestOnSignal(t *testing.T) { s.ClosePrice = decimal.NewFromInt(10) s.Direction = gctorder.Buy + s.Amount = decimal.NewFromInt(1) resp, err = p.OnSignal(s, &exchange.Settings{}, pair) if err != nil { t.Error(err) @@ -737,8 +739,8 @@ func TestCalculatePNL(t *testing.T) { ev.Time = tt0 err = p.UpdatePNL(ev, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received: %v, expected: %v", err, nil) + if !errors.Is(err, gctorder.ErrPositionsNotLoadedForPair) { + t.Errorf("received: %v, expected: %v", err, gctorder.ErrPositionsNotLoadedForPair) } od := &gctorder.Detail{ @@ -784,7 +786,11 @@ func TestCalculatePNL(t *testing.T) { }, }, }, false) - err = p.UpdatePNL(ev, decimal.Zero) + err = s.FuturesTracker.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + err = p.UpdatePNL(ev, decimal.NewFromInt(1)) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } @@ -796,8 +802,8 @@ func TestCalculatePNL(t *testing.T) { if len(pos[0].PNLHistory) == 0 { t.Fatal("expected a pnl entry ( ͡° ͜ʖ ͡°)") } - if !pos[0].RealisedPNL.Equal(decimal.NewFromInt(20)) { + if !pos[0].UnrealisedPNL.Equal(decimal.NewFromInt(26700)) { // 20 orders * $1 difference * 1x leverage - t.Errorf("expected 20, received '%v'", pos[0].RealisedPNL) + t.Errorf("expected 26700, received '%v'", pos[0].UnrealisedPNL) } } diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 19446673e29..9dd36def456 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -22,6 +22,9 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if !ok { return nil, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) } + if o.GetAmount().IsZero() || o.GetAmount().IsNegative() { + return nil, fmt.Errorf("%v %v %v %w invalid order amount %v", o.GetExchange(), o.GetAssetType(), o.Pair(), errNoFunds, o.GetAmount()) + } var amount decimal.Decimal var err error switch retOrder.GetDirection() { diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index 757fe420060..8787e90cd58 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -189,6 +189,11 @@ func TestSizeOrder(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errNoFunds) } + _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + if !errors.Is(err, errNoFunds) { + t.Errorf("received: %v, expected: %v", err, errNoFunds) + } + o.Amount = decimal.NewFromInt(1) _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 18925ef94bb..eb934385300 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -302,10 +302,10 @@ func TestAnalysePNLGrowth(t *testing.T) { ) c.analysePNLGrowth() - if c.HighestRealisedPNL.Value.Equal(decimal.NewFromInt(2)) { + if !c.HighestRealisedPNL.Value.Equal(decimal.NewFromInt(2)) { t.Errorf("received %v expected 2", c.HighestRealisedPNL.Value) } - if c.LowestUnrealisedPNL.Value.Equal(decimal.NewFromInt(1)) { + if !c.LowestUnrealisedPNL.Value.Equal(decimal.NewFromInt(1)) { t.Errorf("received %v expected 1", c.LowestUnrealisedPNL.Value) } @@ -324,10 +324,10 @@ func TestAnalysePNLGrowth(t *testing.T) { ) c.analysePNLGrowth() - if c.HighestRealisedPNL.Value.Equal(decimal.NewFromInt(2)) { + if !c.HighestRealisedPNL.Value.Equal(decimal.NewFromInt(2)) { t.Errorf("received %v expected 2", c.HighestRealisedPNL.Value) } - if c.LowestUnrealisedPNL.Value.Equal(decimal.NewFromFloat(0.5)) { + if !c.LowestUnrealisedPNL.Value.Equal(decimal.NewFromFloat(0.5)) { t.Errorf("received %v expected 0.5", c.LowestUnrealisedPNL.Value) } } diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index 496a85502bf..8b78bf833e6 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -238,8 +238,8 @@ func TestGenerateReport(t *testing.T) { a: { p: &statistics.CurrencyPairStatistic{ MaxDrawdown: statistics.Swing{}, - LowestClosePrice: decimal.NewFromInt(100), - HighestClosePrice: decimal.NewFromInt(200), + LowestClosePrice: statistics.ValueAtTime{Value: decimal.NewFromInt(100)}, + HighestClosePrice: statistics.ValueAtTime{Value: decimal.NewFromInt(200)}, MarketMovement: decimal.NewFromInt(100), StrategyMovement: decimal.NewFromInt(100), CompoundAnnualGrowthRate: decimal.NewFromInt(1), From 400d4457c568aff73ed2b89767b5309999aa6406 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 18 Mar 2022 16:17:05 +1100 Subject: [PATCH 101/171] Test coverage --- backtester/common/common.go | 12 +- backtester/common/common_test.go | 60 +++++++++ backtester/common/common_types.go | 4 +- backtester/data/data.go | 2 +- backtester/data/data_test.go | 41 ++++-- .../eventhandlers/portfolio/portfolio.go | 10 +- .../eventhandlers/portfolio/portfolio_test.go | 127 ++++++++++++++++++ backtester/eventhandlers/statistics/pnl.go | 5 - 8 files changed, 242 insertions(+), 19 deletions(-) delete mode 100644 backtester/eventhandlers/statistics/pnl.go diff --git a/backtester/common/common.go b/backtester/common/common.go index d03ed1c9d62..f7073162f53 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -20,12 +20,22 @@ func DataTypeToInt(dataType string) (int64, error) { // FitStringToLimit ensures a string is of the length of the limit // either by truncating the string with ellipses or padding with the spacer func FitStringToLimit(str, spacer string, limit int, upper bool) string { + if limit < 0 { + return str + } + if limit == 0 { + return "" + } limResp := limit - len(str) if upper { str = strings.ToUpper(str) } if limResp < 0 { - return str[0:limit-3] + "..." + if limit-3 > 0 { + return str[0:limit-3] + "..." + } else { + return str[0:limit] + } } spacerLen := len(spacer) for i := 0; i < limResp; i++ { diff --git a/backtester/common/common_test.go b/backtester/common/common_test.go index 25992ab80ba..c39f321c4db 100644 --- a/backtester/common/common_test.go +++ b/backtester/common/common_test.go @@ -6,6 +6,7 @@ import ( ) func TestDataTypeConversion(t *testing.T) { + t.Parallel() for _, ti := range []struct { title string dataType string @@ -30,6 +31,7 @@ func TestDataTypeConversion(t *testing.T) { }, } { t.Run(ti.title, func(t *testing.T) { + t.Parallel() got, err := DataTypeToInt(ti.dataType) if ti.expectErr { if err == nil { @@ -43,3 +45,61 @@ func TestDataTypeConversion(t *testing.T) { }) } } + +func TestFitStringToLimit(t *testing.T) { + t.Parallel() + for _, ti := range []struct { + str string + sep string + limit int + expected string + upper bool + }{ + { + str: "good", + sep: " ", + limit: 5, + expected: "GOOD ", + upper: true, + }, + { + str: "negative limit", + sep: " ", + limit: -1, + expected: "negative limit", + }, + { + str: "long spacer", + sep: "--", + limit: 14, + expected: "long spacer---", + }, + { + str: "zero limit", + sep: "--", + limit: 0, + expected: "", + }, + { + str: "over limit", + sep: "--", + limit: 6, + expected: "ove...", + }, + { + str: "hi", + sep: " ", + limit: 1, + expected: "h", + }, + } { + test := ti + t.Run(test.str, func(t *testing.T) { + t.Parallel() + result := FitStringToLimit(test.str, test.sep, test.limit, test.upper) + if result != test.expected { + t.Errorf("recieved '%v' expected '%v'", result, test.expected) + } + }) + } +} diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 7d66ace84fd..47ec80aadcf 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -19,6 +19,8 @@ const ( TradeStr = "trade" ) +// custom order side declarations for backtesting processing and +// decision-making const ( // DoNothing is an explicit signal for the backtester to not perform an action // based upon indicator results @@ -138,7 +140,6 @@ type Directioner interface { GetDirection() order.Side } -// colours to display for the terminal output // colours to display for the terminal output var ( ColourDefault = "\u001b[0m" @@ -158,6 +159,7 @@ var ( ColourError = "\033[38;5;196m" ) +// logo and colour storage var ( logo01 = " " logo02 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@ " diff --git a/backtester/data/data.go b/backtester/data/data.go index 7fdd1dd92b8..b35fa93c794 100644 --- a/backtester/data/data.go +++ b/backtester/data/data.go @@ -111,7 +111,7 @@ func (b *Base) List() []common.DataEventHandler { } func (b *Base) IsLastEvent() bool { - return b.latest.GetOffset() == int64(len(b.stream)) + return b.latest != nil && b.latest.GetOffset() == int64(len(b.stream)) } // SortStream sorts the stream by timestamp diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index 8e38f66fb4b..04fd9f2b23b 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -22,25 +22,43 @@ func TestBaseDataFunctions(t *testing.T) { if latest := d.Latest(); latest != nil { t.Error("expected nil") } + d.Next() o := d.Offset() if o != 0 { t.Error("expected 0") } d.AppendStream(nil) + if d.IsLastEvent() { + t.Error("no") + } d.AppendStream(nil) - d.AppendStream(nil) - + if len(d.stream) != 0 { + t.Error("expected 0") + } + d.AppendStream(&fakeDataHandler{time: 1}) + d.AppendStream(&fakeDataHandler{time: 2}) + d.AppendStream(&fakeDataHandler{time: 3}) + d.AppendStream(&fakeDataHandler{time: 4}) + d.Next() + d.Next() + if list := d.List(); len(list) != 2 { + t.Errorf("expected 2 received %v", len(list)) + } + d.Next() d.Next() + if !d.IsLastEvent() { + t.Error("expected last event") + } o = d.Offset() - if o != 0 { - t.Error("expected 0") + if o != 4 { + t.Error("expected 4") } - if list := d.List(); list != nil { - t.Error("expected nil") + if list := d.List(); len(list) != 0 { + t.Error("expected 0") } - if history := d.History(); history != nil { - t.Error("expected nil") + if history := d.History(); len(history) != 4 { + t.Errorf("expected 4 received %v", len(history)) } d.SetStream(nil) if st := d.GetStream(); st != nil { @@ -49,6 +67,7 @@ func TestBaseDataFunctions(t *testing.T) { d.Reset() d.GetStream() d.SortStream() + } func TestSetup(t *testing.T) { @@ -172,7 +191,7 @@ func TestReset(t *testing.T) { // methods that satisfy the common.DataEventHandler interface func (t fakeDataHandler) GetOffset() int64 { - return 0 + return 4 } func (t fakeDataHandler) SetOffset(int64) { @@ -224,3 +243,7 @@ func (t fakeDataHandler) GetLowPrice() decimal.Decimal { func (t fakeDataHandler) GetOpenPrice() decimal.Decimal { return decimal.Zero } + +func (t fakeDataHandler) GetUnderlyingPair() (currency.Pair, error) { + return t.Pair(), nil +} diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b313cd5a9b3..a3b316b2572 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -477,7 +477,7 @@ func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { // GetPositions returns all futures positions for an event's exchange, asset, pair func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStats, error) { if !e.GetAssetType().IsFutures() { - return nil, errors.New("not a future") + return nil, gctorder.ErrNotFuturesAsset } settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) if err != nil { @@ -511,6 +511,12 @@ func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) // TrackFuturesOrder updates the futures tracker with a new order // from a fill event func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) (*PNLSummary, error) { + if f == nil { + return nil, common.ErrNilEvent + } + if fund == nil { + return nil, fmt.Errorf("%w missing funding", common.ErrNilArguments) + } detail := f.GetOrder() if detail == nil { return nil, gctorder.ErrSubmissionIsNil @@ -559,7 +565,7 @@ func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) // if it exists func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, error) { if !e.GetAssetType().IsFutures() { - return nil, errors.New("not a future") + return nil, gctorder.ErrNotFuturesAsset } settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 46032b923ee..8c46122c0e7 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -807,3 +807,130 @@ func TestCalculatePNL(t *testing.T) { t.Errorf("expected 26700, received '%v'", pos[0].UnrealisedPNL) } } + +func TestTrackFuturesOrder(t *testing.T) { + t.Parallel() + p := &Portfolio{} + var expectedError = common.ErrNilEvent + _, err := p.TrackFuturesOrder(nil, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + expectedError = common.ErrNilArguments + _, err = p.TrackFuturesOrder(&fill.Fill{}, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + fundPair := &funding.Pair{} + expectedError = gctorder.ErrSubmissionIsNil + _, err = p.TrackFuturesOrder(&fill.Fill{}, fundPair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + expectedError = gctorder.ErrNotFuturesAsset + od := &gctorder.Detail{} + _, err = p.TrackFuturesOrder(&fill.Fill{ + Order: od, + }, fundPair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + od.AssetType = asset.Futures + expectedError = funding.ErrNotCollateral + _, err = p.TrackFuturesOrder(&fill.Fill{ + Order: od, + }, fundPair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + fundCollateral := &funding.Collateral{} + expectedError = errExchangeUnset + _, err = p.TrackFuturesOrder(&fill.Fill{ + Order: od, + }, fundCollateral) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + cp := currency.NewPair(currency.XRP, currency.DOGE) + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Futures, Pair: cp}) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + od.Pair = cp + od.Exchange = testExchange + od.Side = gctorder.Short + od.AssetType = asset.Futures + od.Amount = 1337 + od.Price = 1337 + od.ID = testExchange + od.Date = time.Now() + expectedError = nil + fundCollateral.Collateral, err = funding.CreateItem(od.Exchange, od.AssetType, od.Pair.Base, decimal.NewFromInt(100), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + fundCollateral.Contract, err = funding.CreateItem(od.Exchange, od.AssetType, od.Pair.Quote, decimal.NewFromInt(100), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + _, err = p.TrackFuturesOrder(&fill.Fill{ + Order: od, + Base: event.Base{ + Exchange: testExchange, + AssetType: asset.Futures, + CurrencyPair: cp, + }, + }, fundCollateral) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + +} + +func TestGetHoldingsForTime(t *testing.T) { + t.Parallel() + +} + +func TestGetPositions(t *testing.T) { + t.Parallel() +} + +func TestGetLatestPNLForEvent(t *testing.T) { + t.Parallel() +} + +func TestGetLatestPNLs(t *testing.T) { + t.Parallel() +} + +func TestGetUnrealisedPNL(t *testing.T) { + t.Parallel() +} + +func TestGetRealisedPNL(t *testing.T) { + t.Parallel() +} + +func TestGetExposure(t *testing.T) { + t.Parallel() +} + +func TestGetCollateralCurrency(t *testing.T) { + t.Parallel() +} + +func TestGetDirection(t *testing.T) { + t.Parallel() +} + +func TestCannotPurchase(t *testing.T) { + t.Parallel() + +} diff --git a/backtester/eventhandlers/statistics/pnl.go b/backtester/eventhandlers/statistics/pnl.go deleted file mode 100644 index 76e6d7b2e7f..00000000000 --- a/backtester/eventhandlers/statistics/pnl.go +++ /dev/null @@ -1,5 +0,0 @@ -package statistics - -func CalculatePNLStatistics() { - -} From bd37e547ed8896f8a7ad99f2ecf611f22a4f44d7 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 21 Mar 2022 11:09:10 +1100 Subject: [PATCH 102/171] Completes portfolio coverage --- .../eventhandlers/portfolio/portfolio.go | 88 +++-- .../eventhandlers/portfolio/portfolio_test.go | 360 ++++++++++++++++++ 2 files changed, 410 insertions(+), 38 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index a3b316b2572..8b91439dfe7 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -75,9 +75,8 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi ev.GetDirection() == "" { return o, nil } - dir := ev.GetDirection() - if !funds.CanPlaceOrder(dir) { - return cannotPurchase(ev, o, dir) + if !funds.CanPlaceOrder(ev.GetDirection()) { + return cannotPurchase(ev, o) } o.Price = ev.GetPrice() @@ -122,7 +121,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi } } if sizingFunds.LessThanOrEqual(decimal.Zero) { - return cannotPurchase(ev, o, dir) + return cannotPurchase(ev, o) } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) sizedOrder.SetDirection(side) @@ -132,8 +131,14 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return p.evaluateOrder(ev, o, sizedOrder) } -func cannotPurchase(ev signal.Event, o *order.Order, dir gctorder.Side) (*order.Order, error) { - o.AppendReason(notEnoughFundsTo + " " + dir.Lower()) +func cannotPurchase(ev signal.Event, o *order.Order) (*order.Order, error) { + if ev == nil { + return nil, common.ErrNilEvent + } + if o == nil { + return nil, fmt.Errorf("%w recieved nil order for %v %v %v", common.ErrNilArguments, ev.GetExchange(), ev.GetAssetType(), ev.Pair()) + } + o.AppendReason(notEnoughFundsTo + " " + ev.GetDirection().Lower()) switch ev.GetDirection() { case gctorder.Sell: o.SetDirection(common.CouldNotSell) @@ -143,6 +148,9 @@ func cannotPurchase(ev signal.Event, o *order.Order, dir gctorder.Side) (*order. o.SetDirection(common.CouldNotShort) case gctorder.Long: o.SetDirection(common.CouldNotLong) + default: + // ensure that unknown scenarios don't affect anything + o.SetDirection(common.DoNothing) } ev.SetDirection(o.Direction) return o, nil @@ -452,39 +460,35 @@ func (p *Portfolio) ViewHoldingAtTimePeriod(ev common.EventHandler) (*holdings.H } // GetLatestHoldings returns the latest holdings after being sorted by time -func (e *Settings) GetLatestHoldings() holdings.Holding { - if len(e.HoldingsSnapshots) == 0 { +func (s *Settings) GetLatestHoldings() holdings.Holding { + if len(s.HoldingsSnapshots) == 0 { return holdings.Holding{} } - return e.HoldingsSnapshots[len(e.HoldingsSnapshots)-1] + return s.HoldingsSnapshots[len(s.HoldingsSnapshots)-1] } // GetHoldingsForTime returns the holdings for a time period, or an empty holding if not found -func (e *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { - if e.HoldingsSnapshots == nil { +func (s *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { + if s.HoldingsSnapshots == nil { // no holdings yet return holdings.Holding{} } - for i := len(e.HoldingsSnapshots) - 1; i >= 0; i-- { - if e.HoldingsSnapshots[i].Timestamp.Equal(t) { - return e.HoldingsSnapshots[i] + for i := len(s.HoldingsSnapshots) - 1; i >= 0; i-- { + if s.HoldingsSnapshots[i].Timestamp.Equal(t) { + return s.HoldingsSnapshots[i] } } return holdings.Holding{} } +var errUnsetFuturesTracker = errors.New("portfolio settings futures tracker unset") + // GetPositions returns all futures positions for an event's exchange, asset, pair func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStats, error) { - if !e.GetAssetType().IsFutures() { - return nil, gctorder.ErrNotFuturesAsset - } - settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + settings, err := p.getFuturesSettingsFromEvent(e) if err != nil { - return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) - } - if settings.FuturesTracker == nil { - return nil, errors.New("no futures tracker") + return nil, err } return settings.FuturesTracker.GetPositions(), nil } @@ -492,14 +496,10 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat // UpdatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) error { - if !e.GetAssetType().IsFutures() { - return fmt.Errorf("%s %w", e.GetAssetType(), gctorder.ErrNotFuturesAsset) - } - settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + settings, err := p.getFuturesSettingsFromEvent(e) if err != nil { - return fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) + return err } - _, err = settings.FuturesTracker.UpdateOpenPositionUnrealisedPNL(closePrice.InexactFloat64(), e.GetTime()) if err != nil && !errors.Is(err, gctorder.ErrPositionClosed) { return err @@ -564,24 +564,17 @@ func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) // GetLatestPNLForEvent takes in an event and returns the latest PNL data // if it exists func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, error) { - if !e.GetAssetType().IsFutures() { - return nil, gctorder.ErrNotFuturesAsset - } - settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + settings, err := p.getFuturesSettingsFromEvent(e) if err != nil { - return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) - } - - if settings.FuturesTracker == nil { - return nil, errors.New("no futures tracker") + return nil, err } - response := &PNLSummary{ Exchange: e.GetExchange(), Item: e.GetAssetType(), Pair: e.Pair(), Offset: e.GetOffset(), } + positions := settings.FuturesTracker.GetPositions() if len(positions) == 0 { return response, nil @@ -595,6 +588,25 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er return response, nil } +func (p *Portfolio) getFuturesSettingsFromEvent(e common.EventHandler) (*Settings, error) { + if e == nil { + return nil, common.ErrNilEvent + } + if !e.GetAssetType().IsFutures() { + return nil, gctorder.ErrNotFuturesAsset + } + settings, err := p.getSettings(e.GetExchange(), e.GetAssetType(), e.Pair()) + if err != nil { + return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) + } + + if settings.FuturesTracker == nil { + return nil, fmt.Errorf("%w for %v %v %v", errUnsetFuturesTracker, e.GetExchange(), e.GetAssetType(), e.Pair()) + } + + return settings, nil +} + func (p *Portfolio) getSettings(exch string, item asset.Item, pair currency.Pair) (*Settings, error) { exchMap, ok := p.exchangeAssetPairSettings[strings.ToLower(exch)] if !ok { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 8c46122c0e7..d1018b01f9c 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -895,42 +895,402 @@ func TestTrackFuturesOrder(t *testing.T) { func TestGetHoldingsForTime(t *testing.T) { t.Parallel() + s := &Settings{} + h := s.GetHoldingsForTime(time.Now()) + if !h.Timestamp.IsZero() { + t.Error("expected unset holdings") + } + tt := time.Now() + s.HoldingsSnapshots = append(s.HoldingsSnapshots, holdings.Holding{ + Timestamp: tt, + Offset: 1337, + }) + h = s.GetHoldingsForTime(time.Unix(1337, 0)) + if !h.Timestamp.IsZero() { + t.Error("expected unset holdings") + } + h = s.GetHoldingsForTime(tt) + if h.Timestamp.IsZero() && h.Offset != 1337 { + t.Error("expected set holdings") + } } func TestGetPositions(t *testing.T) { t.Parallel() + p := &Portfolio{} + var expectedError = common.ErrNilEvent + _, err := p.GetPositions(nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + ev := &fill.Fill{ + Base: event.Base{ + Exchange: testExchange, + CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + }, + } + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: ev.AssetType, Pair: ev.Pair()}) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + expectedError = nil + _, err = p.GetPositions(ev) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } } func TestGetLatestPNLForEvent(t *testing.T) { t.Parallel() + p := &Portfolio{} + var expectedError = common.ErrNilEvent + _, err := p.GetLatestPNLForEvent(nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + ev := &fill.Fill{ + Base: event.Base{ + Exchange: testExchange, + CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + }, + } + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: ev.AssetType, Pair: ev.Pair()}) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + expectedError = nil + _, err = p.GetLatestPNLForEvent(ev) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + settings, ok := p.exchangeAssetPairSettings[ev.GetExchange()][ev.GetAssetType()][ev.Pair()] + if !ok { + t.Fatalf("where did settings go?") + } + err = settings.FuturesTracker.TrackNewOrder(&gctorder.Detail{ + Exchange: ev.GetExchange(), + AssetType: ev.AssetType, + Pair: ev.Pair(), + Amount: 1, + Price: 1, + ID: "one", + Date: time.Now(), + Side: gctorder.Buy, + }) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + latest, err := p.GetLatestPNLForEvent(ev) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if latest == nil { + t.Error("unexpected") + } +} + +func TestGetFuturesSettingsFromEvent(t *testing.T) { + t.Parallel() + p := &Portfolio{} + var expectedError = common.ErrNilEvent + _, err := p.getFuturesSettingsFromEvent(nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + expectedError = gctorder.ErrNotFuturesAsset + _, err = p.getFuturesSettingsFromEvent(&fill.Fill{}) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + ev := &fill.Fill{ + Base: event.Base{ + Exchange: testExchange, + CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + }, + } + expectedError = errExchangeUnset + _, err = p.getFuturesSettingsFromEvent(ev) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + ff := &ftx.FTX{} + ff.Name = testExchange + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: ev.AssetType, Pair: ev.Pair()}) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + expectedError = nil + settings, err := p.getFuturesSettingsFromEvent(ev) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + expectedError = errUnsetFuturesTracker + settings.FuturesTracker = nil + _, err = p.getFuturesSettingsFromEvent(ev) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } } func TestGetLatestPNLs(t *testing.T) { t.Parallel() + p := &Portfolio{} + latest := p.GetLatestPNLs() + if len(latest) != 0 { + t.Error("expected empty") + } + ev := &fill.Fill{ + Base: event.Base{ + Exchange: testExchange, + CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), + AssetType: asset.Futures, + }, + } + ff := &ftx.FTX{} + ff.Name = testExchange + err := p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: ev.AssetType, Pair: ev.Pair()}) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + settings, ok := p.exchangeAssetPairSettings[ev.GetExchange()][ev.GetAssetType()][ev.Pair()] + if !ok { + t.Fatalf("where did settings go?") + } + err = settings.FuturesTracker.TrackNewOrder(&gctorder.Detail{ + Exchange: ev.GetExchange(), + AssetType: ev.AssetType, + Pair: ev.Pair(), + Amount: 1, + Price: 1, + ID: "one", + Date: time.Now(), + Side: gctorder.Buy, + }) + if !errors.Is(err, nil) { + t.Fatalf("received '%v' expected '%v'", err, nil) + } + latest = p.GetLatestPNLs() + if len(latest) != 1 { + t.Error("expected 1") + } } func TestGetUnrealisedPNL(t *testing.T) { t.Parallel() + p := PNLSummary{ + Exchange: testExchange, + Item: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + CollateralCurrency: currency.USD, + Offset: 1, + Result: gctorder.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1338), + RealisedPNL: decimal.NewFromInt(1339), + Price: decimal.NewFromInt(1331), + Exposure: decimal.NewFromInt(1332), + Direction: gctorder.Short, + Fee: decimal.NewFromInt(1333), + IsLiquidated: true, + }, + } + result := p.GetUnrealisedPNL() + if !result.PNL.Equal(p.Result.UnrealisedPNL) { + t.Errorf("received '%v' expected '%v'", result.PNL, p.Result.UnrealisedPNL) + } + if !result.Time.Equal(p.Result.Time) { + t.Errorf("received '%v' expected '%v'", result.Time, p.Result.Time) + } + if !result.Currency.Equal(p.CollateralCurrency) { + t.Errorf("received '%v' expected '%v'", result.Currency, p.CollateralCurrency) + } } func TestGetRealisedPNL(t *testing.T) { t.Parallel() + p := PNLSummary{ + Exchange: testExchange, + Item: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + CollateralCurrency: currency.USD, + Offset: 1, + Result: gctorder.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1338), + RealisedPNL: decimal.NewFromInt(1339), + Price: decimal.NewFromInt(1331), + Exposure: decimal.NewFromInt(1332), + Direction: gctorder.Short, + Fee: decimal.NewFromInt(1333), + IsLiquidated: true, + }, + } + result := p.GetRealisedPNL() + if !result.PNL.Equal(p.Result.RealisedPNL) { + t.Errorf("received '%v' expected '%v'", result.PNL, p.Result.RealisedPNL) + } + if !result.Time.Equal(p.Result.Time) { + t.Errorf("received '%v' expected '%v'", result.Time, p.Result.Time) + } + if !result.Currency.Equal(p.CollateralCurrency) { + t.Errorf("received '%v' expected '%v'", result.Currency, p.CollateralCurrency) + } } func TestGetExposure(t *testing.T) { t.Parallel() + p := PNLSummary{ + Exchange: testExchange, + Item: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + CollateralCurrency: currency.USD, + Offset: 1, + Result: gctorder.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1338), + RealisedPNL: decimal.NewFromInt(1339), + Price: decimal.NewFromInt(1331), + Exposure: decimal.NewFromInt(1332), + Direction: gctorder.Short, + Fee: decimal.NewFromInt(1333), + IsLiquidated: true, + }, + } + result := p.GetExposure() + if !result.Equal(p.Result.Exposure) { + t.Errorf("received '%v' expected '%v'", result, p.Result.Exposure) + } } func TestGetCollateralCurrency(t *testing.T) { t.Parallel() + p := PNLSummary{ + Exchange: testExchange, + Item: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + CollateralCurrency: currency.USD, + Offset: 1, + Result: gctorder.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1338), + RealisedPNL: decimal.NewFromInt(1339), + Price: decimal.NewFromInt(1331), + Exposure: decimal.NewFromInt(1332), + Direction: gctorder.Short, + Fee: decimal.NewFromInt(1333), + IsLiquidated: true, + }, + } + result := p.GetCollateralCurrency() + if !result.Equal(p.CollateralCurrency) { + t.Errorf("received '%v' expected '%v'", result, p.CollateralCurrency) + } } func TestGetDirection(t *testing.T) { t.Parallel() + p := PNLSummary{ + Exchange: testExchange, + Item: asset.Futures, + Pair: currency.NewPair(currency.BTC, currency.USDT), + CollateralCurrency: currency.USD, + Offset: 1, + Result: gctorder.PNLResult{ + Time: time.Now(), + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1338), + RealisedPNL: decimal.NewFromInt(1339), + Price: decimal.NewFromInt(1331), + Exposure: decimal.NewFromInt(1332), + Direction: gctorder.Short, + Fee: decimal.NewFromInt(1333), + IsLiquidated: true, + }, + } + result := p.GetDirection() + if result != (p.Result.Direction) { + t.Errorf("received '%v' expected '%v'", result, p.Result.Direction) + } } func TestCannotPurchase(t *testing.T) { t.Parallel() + var expectedError = common.ErrNilEvent + _, err := cannotPurchase(nil, nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + s := &signal.Signal{} + expectedError = common.ErrNilArguments + _, err = cannotPurchase(s, nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + o := &order.Order{} + s.Direction = gctorder.Buy + expectedError = nil + result, err := cannotPurchase(s, o) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if result.Direction != common.CouldNotBuy { + t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotBuy) + } + + s.Direction = gctorder.Sell + expectedError = nil + result, err = cannotPurchase(s, o) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if result.Direction != common.CouldNotSell { + t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotSell) + } + + s.Direction = gctorder.Short + expectedError = nil + result, err = cannotPurchase(s, o) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if result.Direction != common.CouldNotShort { + t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotShort) + } + s.Direction = gctorder.Long + expectedError = nil + result, err = cannotPurchase(s, o) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if result.Direction != common.CouldNotLong { + t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotLong) + } + + s.Direction = gctorder.UnknownSide + expectedError = nil + result, err = cannotPurchase(s, o) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if result.Direction != common.DoNothing { + t.Errorf("received '%v' expected '%v'", result.Direction, common.DoNothing) + } } From 20e5ea65d249ada53a41d289d4bd335acc5692dd Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 22 Mar 2022 14:37:18 +1100 Subject: [PATCH 103/171] Increase coverage exchange, portfolio. fix size bug. NEW CHART --- backtester/engine/backtest.go | 2 +- backtester/eventhandlers/exchange/exchange.go | 93 ++++++----- .../eventhandlers/exchange/exchange_test.go | 145 ++++++++++++++++-- .../eventhandlers/portfolio/portfolio.go | 4 +- .../eventhandlers/portfolio/size/size.go | 3 - .../statistics/fundingstatistics_test.go | 13 ++ .../eventhandlers/statistics/statistics.go | 7 +- .../statistics/statistics_types.go | 1 - .../strategies/base/base_test.go | 26 ++++ backtester/funding/collateral.go | 93 ++++------- backtester/funding/funding_test.go | 49 ++++-- backtester/funding/funding_types.go | 6 +- backtester/funding/item.go | 57 +++++++ backtester/funding/pair.go | 14 +- backtester/report/report.go | 38 +++++ backtester/report/report_test.go | 5 +- backtester/report/report_types.go | 1 + backtester/report/tpl.gohtml | 74 ++++++++- 18 files changed, 473 insertions(+), 158 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 9a36d1ed76e..66ee644886d 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -225,7 +225,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if ev.GetAssetType().IsFutures() { var cr funding.ICollateralReleaser - cr, err = funds.GetCollateralReleaser() + cr, err = funds.CollateralReleaser() if err != nil { return err } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index cc1819e4f03..ae278026d0e 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -136,13 +136,50 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.ExchangeFee = calculateExchangeFee(adjustedPrice, limitReducedAmount, cs.ExchangeFee) orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) - switch cs.Asset { + err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) + if err != nil { + return f, err + } + + ords := orderManager.GetOrdersSnapshot("") + for i := range ords { + if ords[i].ID != orderID { + continue + } + ords[i].Date = o.GetTime() + ords[i].LastUpdated = o.GetTime() + ords[i].CloseTime = o.GetTime() + f.Order = &ords[i] + f.PurchasePrice = decimal.NewFromFloat(ords[i].Price) + f.Amount = decimal.NewFromFloat(ords[i].Amount) + if ords[i].Fee > 0 { + f.ExchangeFee = decimal.NewFromFloat(ords[i].Fee) + } + f.Total = f.PurchasePrice.Mul(f.Amount).Add(f.ExchangeFee) + } + + if f.Order == nil { + return nil, fmt.Errorf("placed order %v not found in order manager", orderID) + } + + return f, nil +} + +func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderError error, orderAmount, eventFunds, limitReducedAmount, adjustedPrice decimal.Decimal) error { + if f == nil { + return fmt.Errorf("%w: fill event", common.ErrNilEvent) + } + if funds == nil { + return fmt.Errorf("%w: funding", common.ErrNilArguments) + } + + switch f.AssetType { case asset.Spot: - pr, fundErr := funds.GetPairReleaser() + pr, fundErr := funds.PairReleaser() if fundErr != nil { - return f, fundErr + return fundErr } - if err != nil { + if orderError != nil { fundErr = pr.Release(eventFunds, eventFunds, f.GetDirection()) if fundErr != nil { f.AppendReason(fundErr.Error()) @@ -152,64 +189,48 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } else if f.GetDirection() == gctorder.Sell { f.SetDirection(common.CouldNotSell) } - return f, err + return orderError } switch f.GetDirection() { case gctorder.Buy: fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) if fundErr != nil { - return f, fundErr + return fundErr } pr.IncreaseAvailable(limitReducedAmount, f.GetDirection()) case gctorder.Sell: fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) if fundErr != nil { - return f, fundErr + return fundErr } pr.IncreaseAvailable(limitReducedAmount.Mul(adjustedPrice), f.GetDirection()) + default: + return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.GetDirection()) } case asset.Futures: - cr, fundErr := funds.GetCollateralReleaser() + cr, fundErr := funds.CollateralReleaser() if fundErr != nil { - return f, fundErr + return fundErr } - if err != nil { - fundErr = cr.ReleaseContracts(o.GetAmount()) + if orderError != nil { + fundErr = cr.ReleaseContracts(orderAmount) if fundErr != nil { - return f, fundErr + return fundErr } switch f.GetDirection() { case gctorder.Short: f.SetDirection(common.CouldNotShort) case gctorder.Long: f.SetDirection(common.CouldNotLong) + default: + return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.GetDirection()) } - return f, err - } - } - - ords := orderManager.GetOrdersSnapshot("") - for i := range ords { - if ords[i].ID != orderID { - continue + return orderError } - ords[i].Date = o.GetTime() - ords[i].LastUpdated = o.GetTime() - ords[i].CloseTime = o.GetTime() - f.Order = &ords[i] - f.PurchasePrice = decimal.NewFromFloat(ords[i].Price) - f.Amount = decimal.NewFromFloat(ords[i].Amount) - if ords[i].Fee > 0 { - f.ExchangeFee = decimal.NewFromFloat(ords[i].Fee) - } - f.Total = f.PurchasePrice.Mul(f.Amount).Add(f.ExchangeFee) - } - - if f.Order == nil { - return nil, fmt.Errorf("placed order %v not found in order manager", orderID) + default: + return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.AssetType) } - - return f, nil + return nil } // verifyOrderWithinLimits conforms the amount to fall into the minimum size and maximum size limit after reduced diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 050ad478587..5b52612ea69 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -35,7 +35,7 @@ func (f *fakeFund) GetCollateralReader() (funding.ICollateralReader, error) { return nil, nil } -func (f *fakeFund) GetPairReleaser() (funding.IPairReleaser, error) { +func (f *fakeFund) PairReleaser() (funding.IPairReleaser, error) { btc, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(9999), decimal.NewFromInt(9999)) if err != nil { return nil, err @@ -58,7 +58,7 @@ func (f *fakeFund) GetPairReleaser() (funding.IPairReleaser, error) { } return p, nil } -func (f *fakeFund) GetCollateralReleaser() (funding.ICollateralReleaser, error) { +func (f *fakeFund) CollateralReleaser() (funding.ICollateralReleaser, error) { return nil, nil } @@ -266,22 +266,23 @@ func TestExecuteOrder(t *testing.T) { AllocatedSize: decimal.NewFromInt(1337), } - d := &kline.DataFromKline{ - Item: gctkline.Item{ - Exchange: testExchange, - Pair: p, - Asset: a, - Interval: 0, - Candles: []gctkline.Candle{ - { - Close: 1, - High: 1, - Low: 1, - Volume: 1, - }, + item := gctkline.Item{ + Exchange: testExchange, + Pair: p, + Asset: a, + Interval: 0, + Candles: []gctkline.Candle{ + { + Close: 1, + High: 1, + Low: 1, + Volume: 1, }, }, } + d := &kline.DataFromKline{ + Item: item, + } err = d.Load() if err != nil { t.Error(err) @@ -565,3 +566,117 @@ func TestVerifyOrderWithinLimits(t *testing.T) { t.Errorf("received %v expected %v", err, errExceededPortfolioLimit) } } + +func TestAllocateFundsPostOrder(t *testing.T) { + t.Parallel() + expectedError := common.ErrNilEvent + err := allocateFundsPostOrder(nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + expectedError = common.ErrNilArguments + f := &fill.Fill{ + Base: event.Base{ + AssetType: asset.Spot, + }, + Direction: gctorder.Buy, + } + err = allocateFundsPostOrder(f, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + expectedError = nil + one := decimal.NewFromInt(1) + item, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + item2, err := funding.CreateItem(testExchange, asset.Spot, currency.USD, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = item.Reserve(one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = item2.Reserve(one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + fundPair := &funding.Pair{ + Base: item, + Quote: item2, + } + expectedError = nil + err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + f.SetDirection(gctorder.Sell) + err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + expectedError = gctorder.ErrSubmissionIsNil + orderError := gctorder.ErrSubmissionIsNil + err = allocateFundsPostOrder(f, fundPair, orderError, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + f.AssetType = asset.Futures + f.SetDirection(gctorder.Short) + expectedError = nil + item3, err := funding.CreateItem(testExchange, asset.Futures, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + item4, err := funding.CreateItem(testExchange, asset.Futures, currency.USD, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = item3.Reserve(one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = item4.Reserve(one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + collateralPair := &funding.Collateral{ + Contract: item3, + Collateral: item4, + } + expectedError = gctorder.ErrSubmissionIsNil + err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + expectedError = nil + err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + expectedError = gctorder.ErrSubmissionIsNil + f.SetDirection(gctorder.Long) + err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + expectedError = nil + err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + f.AssetType = asset.Margin + expectedError = common.ErrInvalidDataType + err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } +} diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 8b91439dfe7..8671450283b 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -254,7 +254,7 @@ func (p *Portfolio) OnFill(ev fill.Event, funds funding.IFundReleaser) (fill.Eve var err error if ev.GetAssetType() == asset.Spot { - fp, err := funds.GetPairReleaser() + fp, err := funds.PairReleaser() if err != nil { return nil, err } @@ -525,7 +525,7 @@ func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) return nil, fmt.Errorf("order '%v' %w", detail.ID, gctorder.ErrNotFuturesAsset) } - collateralReleaser, err := fund.GetCollateralReleaser() + collateralReleaser, err := fund.CollateralReleaser() if err != nil { return nil, fmt.Errorf("%v %v %v %w", detail.Exchange, detail.AssetType, detail.Pair, err) } diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 9dd36def456..19446673e29 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -22,9 +22,6 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if !ok { return nil, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) } - if o.GetAmount().IsZero() || o.GetAmount().IsNegative() { - return nil, fmt.Errorf("%v %v %v %w invalid order amount %v", o.GetExchange(), o.GetAssetType(), o.Pair(), errNoFunds, o.GetAmount()) - } var amount decimal.Decimal var err error switch retOrder.GetDirection() { diff --git a/backtester/eventhandlers/statistics/fundingstatistics_test.go b/backtester/eventhandlers/statistics/fundingstatistics_test.go index 09efa162093..7804f48512f 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics_test.go +++ b/backtester/eventhandlers/statistics/fundingstatistics_test.go @@ -181,6 +181,19 @@ func TestCalculateIndividualFundingStatistics(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } + + ri.Asset = asset.Futures + _, err = CalculateIndividualFundingStatistics(false, ri, rs) + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } + + ri.IsCollateral = true + _, err = CalculateIndividualFundingStatistics(false, ri, rs) + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } + } func TestFundingStatisticsPrintResults(t *testing.T) { diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index c8e685a88d6..a307a2761e9 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -237,15 +237,14 @@ func (s *Statistic) CalculateAllResults() error { // GetBestMarketPerformer returns the best final market movement func (s *Statistic) GetBestMarketPerformer(results []FinalResultsHolder) *FinalResultsHolder { - result := &FinalResultsHolder{} + var result FinalResultsHolder for i := range results { if results[i].MarketMovement.GreaterThan(result.MarketMovement) || result.MarketMovement.IsZero() { - result = &results[i] - break + result = results[i] } } - return result + return &result } // GetBestStrategyPerformer returns the best performing strategy result diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 295ef0a3e3a..0fb6c4fb238 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -50,7 +50,6 @@ type Statistic struct { BiggestDrawdown *FinalResultsHolder `json:"biggest-drawdown,omitempty"` BestStrategyResults *FinalResultsHolder `json:"best-start-results,omitempty"` BestMarketMovement *FinalResultsHolder `json:"best-market-movement,omitempty"` - CurrencyPairStatistics []CurrencyPairStatistic `json:"currency-pair-statistics"` // as ExchangeAssetPairStatistics cannot be rendered via json.Marshall, we append all result to this slice instead WasAnyDataMissing bool `json:"was-any-data-missing"` FundingStatistics *FundingStatistics `json:"funding-statistics"` FundManager funding.IFundingManager `json:"-"` diff --git a/backtester/eventhandlers/strategies/base/base_test.go b/backtester/eventhandlers/strategies/base/base_test.go index 091fca63f6b..e82af1296b2 100644 --- a/backtester/eventhandlers/strategies/base/base_test.go +++ b/backtester/eventhandlers/strategies/base/base_test.go @@ -17,6 +17,7 @@ import ( ) func TestGetBase(t *testing.T) { + t.Parallel() s := Strategy{} _, err := s.GetBaseData(nil) if !errors.Is(err, common.ErrNilArguments) { @@ -59,6 +60,7 @@ func TestGetBase(t *testing.T) { } func TestSetSimultaneousProcessing(t *testing.T) { + t.Parallel() s := Strategy{} is := s.UsingSimultaneousProcessing() if is { @@ -70,3 +72,27 @@ func TestSetSimultaneousProcessing(t *testing.T) { t.Error("expected true") } } + +func TestUsingExchangeLevelFunding(t *testing.T) { + t.Parallel() + s := &Strategy{} + if s.UsingExchangeLevelFunding() { + t.Error("expected false") + } + s.usingExchangeLevelFunding = true + if !s.UsingExchangeLevelFunding() { + t.Error("expected true") + } +} + +func TestSetExchangeLevelFunding(t *testing.T) { + t.Parallel() + s := &Strategy{} + s.SetExchangeLevelFunding(true) + if !s.UsingExchangeLevelFunding() { + t.Error("expected true") + } + if !s.UsingExchangeLevelFunding() { + t.Error("expected true") + } +} diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 03a6c5d97c1..50f947a4f20 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -10,19 +10,20 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) +// collateral related errors var ( - // ErrNotCollateral is returned when a user requests collateral from a non-collateral pair ErrNotCollateral = errors.New("not a collateral pair") + ErrIsCollateral = errors.New("is collateral pair") ErrNilPair = errors.New("nil pair") + errPositiveOnly = errors.New("reduces the amount by subtraction, positive numbers only") ) -// TODO consider moving futures tracking to funding -// we're already passing around funding items, it can then also have all the lovely tracking attached? - +// CanPlaceOrder checks if there is any collateral to spare func (c *Collateral) CanPlaceOrder(_ order.Side) bool { return c.Collateral.CanPlaceOrder() } +// TakeProfit handles both the reduction of contracts and the change in collateral func (c *Collateral) TakeProfit(contracts, positionReturns decimal.Decimal) error { err := c.Contract.ReduceContracts(contracts) if err != nil { @@ -31,39 +32,37 @@ func (c *Collateral) TakeProfit(contracts, positionReturns decimal.Decimal) erro return c.Collateral.TakeProfit(positionReturns) } +// ContractCurrency returns the contract currency func (c *Collateral) ContractCurrency() currency.Code { return c.Contract.currency } -func (c *Collateral) UnderlyingAsset() currency.Code { - // somehow get the underlying - return c.Contract.currency -} - +// CollateralCurrency returns collateral currency func (c *Collateral) CollateralCurrency() currency.Code { return c.Collateral.currency } +// InitialFunds returns initial funds of collateral func (c *Collateral) InitialFunds() decimal.Decimal { return c.Collateral.initialFunds } +// AvailableFunds returns available funds of collateral func (c *Collateral) AvailableFunds() decimal.Decimal { return c.Collateral.available } +// GetPairReader returns an error because collateral isn't a pair func (c *Collateral) GetPairReader() (IPairReader, error) { return nil, fmt.Errorf("could not return pair reader for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } +// GetCollateralReader returns a collateral reader interface of Collateral func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { return c, nil } -func (c *Collateral) UpdateCollateral(amount decimal.Decimal) error { - return c.Collateral.TakeProfit(amount) -} - +// UpdateContracts adds or subtracts contracts based on order direction func (c *Collateral) UpdateContracts(s order.Side, amount decimal.Decimal) error { switch { case c.currentDirection == nil: @@ -78,70 +77,34 @@ func (c *Collateral) UpdateContracts(s order.Side, amount decimal.Decimal) error } } -func (i *Item) TakeProfit(amount decimal.Decimal) error { - if !i.asset.IsFutures() { - return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) - } - i.available = i.available.Add(amount) - return nil -} - -// AddContracts allocates an amount of funds to be used at a later time -// it prevents multiple events from claiming the same resource -func (i *Item) AddContracts(amount decimal.Decimal) error { - if !i.asset.IsFutures() { - return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) - } - if amount.LessThanOrEqual(decimal.Zero) { - return errZeroAmountReceived - } - i.available = i.available.Add(amount) - return nil -} - -// ReduceContracts allocates an amount of funds to be used at a later time -// it prevents multiple events from claiming the same resource -func (i *Item) ReduceContracts(amount decimal.Decimal) error { - if !i.asset.IsFutures() { - return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) +// ReleaseContracts lowers the amount of available contracts +func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { + if amount.LessThan(decimal.Zero) { + return fmt.Errorf("release %w", errPositiveOnly) } - if amount.LessThanOrEqual(decimal.Zero) { - return errZeroAmountReceived + if c.Contract.available.LessThan(amount) { + return fmt.Errorf("%w amount '%v' larger than available '%v'", errCannotAllocate, amount, c.Contract.available) } - if amount.GreaterThan(i.available) { - return fmt.Errorf("%w for %v %v %v. Requested %v Reserved: %v", - errCannotAllocate, - i.exchange, - i.asset, - i.currency, - amount, - i.reserved) - } - i.available = i.available.Sub(amount) - return nil -} - -func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { - // turn this into a protected func c.Contract.available = c.Contract.available.Sub(amount) return nil } -// FundReader +// FundReader returns a fund reader interface of collateral func (c *Collateral) FundReader() IFundReader { return c } -// FundReserver +// FundReserver returns a fund reserver interface of Collateral func (c *Collateral) FundReserver() IFundReserver { return c } -// GetPairReleaser -func (c *Collateral) GetPairReleaser() (IPairReleaser, error) { +// PairReleaser returns an error as there is no such thing for collateral +func (c *Collateral) PairReleaser() (IPairReleaser, error) { return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } +// Reserve reserves or releases collateral based on order side func (c *Collateral) Reserve(amount decimal.Decimal, side order.Side) error { switch side { case order.Long, order.Short: @@ -158,21 +121,25 @@ func (c *Collateral) Reserve(amount decimal.Decimal, side order.Side) error { } } -// GetCollateralReleaser -func (c *Collateral) GetCollateralReleaser() (ICollateralReleaser, error) { +// CollateralReleaser returns an ICollateralReleaser to interact with +// collateral +func (c *Collateral) CollateralReleaser() (ICollateralReleaser, error) { return c, nil } -// FundReleaser +// FundReleaser returns an IFundReleaser to interact with +// collateral func (c *Collateral) FundReleaser() IFundReleaser { return c } +// Liquidate kills your funds and future func (c *Collateral) Liquidate() { c.Collateral.available = decimal.Zero c.Contract.available = decimal.Zero } +// CurrentHoldings returns available contract holdings func (c *Collateral) CurrentHoldings() decimal.Decimal { return c.Contract.available } diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index f17715cc51d..fb867c7e466 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -26,6 +26,21 @@ var ( pair = currency.NewPair(base, quote) ) +// fakeEvent implements common.EventHandler without +// caring about the response, or dealing with import cycles +type fakeEvent struct{} + +func (f *fakeEvent) GetOffset() int64 { return 0 } +func (f *fakeEvent) SetOffset(int64) {} +func (f *fakeEvent) IsEvent() bool { return true } +func (f *fakeEvent) GetTime() time.Time { return time.Now() } +func (f *fakeEvent) Pair() currency.Pair { return pair } +func (f *fakeEvent) GetExchange() string { return exch } +func (f *fakeEvent) GetInterval() gctkline.Interval { return gctkline.OneMin } +func (f *fakeEvent) GetAssetType() asset.Item { return asset.Spot } +func (f *fakeEvent) GetReason() string { return "" } +func (f *fakeEvent) AppendReason(string) {} + func TestSetupFundingManager(t *testing.T) { t.Parallel() f, err := SetupFundingManager(&engine.ExchangeManager{}, true, false) @@ -286,21 +301,6 @@ func TestAddPair(t *testing.T) { } } -// fakeEvent implements common.EventHandler without -// caring about the response, or dealing with import cycles -type fakeEvent struct{} - -func (f *fakeEvent) GetOffset() int64 { return 0 } -func (f *fakeEvent) SetOffset(int64) {} -func (f *fakeEvent) IsEvent() bool { return true } -func (f *fakeEvent) GetTime() time.Time { return time.Now() } -func (f *fakeEvent) Pair() currency.Pair { return pair } -func (f *fakeEvent) GetExchange() string { return exch } -func (f *fakeEvent) GetInterval() gctkline.Interval { return gctkline.OneMin } -func (f *fakeEvent) GetAssetType() asset.Item { return asset.Spot } -func (f *fakeEvent) GetReason() string { return "" } -func (f *fakeEvent) AppendReason(string) {} - func TestGetFundingForEvent(t *testing.T) { t.Parallel() e := &fakeEvent{} @@ -858,6 +858,7 @@ func TestMatchesCurrency(t *testing.T) { } func TestCreateSnapshot(t *testing.T) { + t.Parallel() f := FundManager{} f.CreateSnapshot(time.Time{}) f.items = append(f.items, &Item{ @@ -900,6 +901,7 @@ func TestCreateSnapshot(t *testing.T) { } func TestAddUSDTrackingData(t *testing.T) { + t.Parallel() f := FundManager{} err := f.AddUSDTrackingData(nil) if !errors.Is(err, common.ErrNilArguments) { @@ -982,6 +984,7 @@ func TestAddUSDTrackingData(t *testing.T) { } func TestUSDTrackingDisabled(t *testing.T) { + t.Parallel() f := FundManager{} if f.USDTrackingDisabled() { t.Error("received true, expected false") @@ -991,3 +994,19 @@ func TestUSDTrackingDisabled(t *testing.T) { t.Error("received false, expected true") } } + +func TestCanPlaceOrder(t *testing.T) { + t.Parallel() + item := &Item{} + c := &Collateral{ + Contract: item, + Collateral: item, + } + if c.CanPlaceOrder(gctorder.Buy) { + t.Error("expected false") + } + c.Collateral.available = decimal.NewFromInt(1337) + if !c.CanPlaceOrder(gctorder.Buy) { + t.Error("expected true") + } +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index ec10a7ca8ff..92449e24a3d 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -80,8 +80,8 @@ type IFundReserver interface { // or release pair or collateral funds type IFundReleaser interface { IFundReader - GetPairReleaser() (IPairReleaser, error) - GetCollateralReleaser() (ICollateralReleaser, error) + PairReleaser() (IPairReleaser, error) + CollateralReleaser() (ICollateralReleaser, error) } // IPairReader is used to limit pair funding functions @@ -96,7 +96,6 @@ type IPairReader interface { // ICollateralReader is used to read data from // collateral pairs type ICollateralReader interface { - UnderlyingAsset() currency.Code ContractCurrency() currency.Code CollateralCurrency() currency.Code InitialFunds() decimal.Decimal @@ -113,7 +112,6 @@ type IPairReleaser interface { type ICollateralReleaser interface { ICollateralReader - UpdateCollateral(decimal.Decimal) error UpdateContracts(order.Side, decimal.Decimal) error TakeProfit(contracts, positionReturns decimal.Decimal) error ReleaseContracts(decimal.Decimal) error diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 0fc1aa81307..f4a58b03cfd 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -114,3 +114,60 @@ func (i *Item) MatchesItemCurrency(item *Item) bool { func (i *Item) MatchesExchange(item *Item) bool { return i != nil && item != nil && i.exchange == item.exchange } + +// TakeProfit increases available funds for a futures asset +func (i *Item) TakeProfit(amount decimal.Decimal) error { + if !i.asset.IsFutures() { + return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) + } + if !i.isCollateral { + return fmt.Errorf("%v %v %v %w cannot add profit to contracts", i.exchange, i.asset, i.currency, ErrNotCollateral) + } + summed := i.available.Add(amount) + if summed.LessThan(decimal.Zero) { + return fmt.Errorf("%w amount '%v' would take available funds '%v' to '%v'", errNotEnoughFunds, amount, i.available, summed) + } + i.available = summed + return nil +} + +// AddContracts allocates an amount of funds to be used at a later time +// it prevents multiple events from claiming the same resource +func (i *Item) AddContracts(amount decimal.Decimal) error { + if !i.asset.IsFutures() { + return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) + } + if i.isCollateral { + return fmt.Errorf("%v %v %v %w cannot add contracts to collateral", i.exchange, i.asset, i.currency, ErrIsCollateral) + } + if amount.LessThanOrEqual(decimal.Zero) { + return errZeroAmountReceived + } + i.available = i.available.Add(amount) + return nil +} + +// ReduceContracts allocates an amount of funds to be used at a later time +// it prevents multiple events from claiming the same resource +func (i *Item) ReduceContracts(amount decimal.Decimal) error { + if !i.asset.IsFutures() { + return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) + } + if i.isCollateral { + return fmt.Errorf("%v %v %v %w cannot add contracts to collateral", i.exchange, i.asset, i.currency, ErrIsCollateral) + } + if amount.LessThanOrEqual(decimal.Zero) { + return errZeroAmountReceived + } + if amount.GreaterThan(i.available) { + return fmt.Errorf("%w for %v %v %v. Requested %v Reserved: %v", + errCannotAllocate, + i.exchange, + i.asset, + i.currency, + amount, + i.reserved) + } + i.available = i.available.Sub(amount) + return nil +} diff --git a/backtester/funding/pair.go b/backtester/funding/pair.go index 0e5ad1ff397..e926b9bc432 100644 --- a/backtester/funding/pair.go +++ b/backtester/funding/pair.go @@ -107,30 +107,30 @@ func (p *Pair) CanPlaceOrder(side order.Side) bool { return false } -// FundReader +// FundReader returns a fund reader interface of the pair func (p *Pair) FundReader() IFundReader { return p } -// FundReserver +// FundReserver returns a fund reserver interface of the pair func (p *Pair) FundReserver() IFundReserver { return p } -// GetPairReleaser -func (p *Pair) GetPairReleaser() (IPairReleaser, error) { +// PairReleaser returns a pair releaser interface of the pair +func (p *Pair) PairReleaser() (IPairReleaser, error) { if p == nil { return nil, ErrNilPair } return p, nil } -// GetCollateralReleaser -func (p *Pair) GetCollateralReleaser() (ICollateralReleaser, error) { +// CollateralReleaser returns an error because a pair is not collateral +func (p *Pair) CollateralReleaser() (ICollateralReleaser, error) { return nil, ErrNotCollateral } -// FundReleaser +// FundReleaser returns a pair releaser interface of the pair func (p *Pair) FundReleaser() IFundReleaser { return p } diff --git a/backtester/report/report.go b/backtester/report/report.go index ffdbbea1fc3..b8178c33917 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -44,6 +44,7 @@ func (d *Data) GenerateReport() error { } d.USDTotalsChart = d.CreateUSDTotalsChart() d.HoldingsOverTimeChart = d.CreateHoldingsOverTimeChart() + d.PNLOverTimeChart = d.createPNLCharts() tmpl := template.Must( template.ParseFiles( @@ -159,6 +160,43 @@ func (d *Data) UpdateItem(k *kline.Item) { } } +func (d *Data) createPNLCharts() []TotalsChart { + var resp []TotalsChart + for exch, assetMap := range d.Statistics.ExchangeAssetPairStatistics { + for item, pairMap := range assetMap { + for pair, result := range pairMap { + id := fmt.Sprintf("%v %v %v", + exch, + item, + pair) + uPNLName := fmt.Sprintf("%v Unrealised PNL", id) + rPNLName := fmt.Sprintf("%v Realised PNL", id) + + unrealisedPNL := TotalsChart{Name: uPNLName} + realisedPNL := TotalsChart{Name: rPNLName} + for i := range result.Events { + if result.Events[i].PNL != nil { + realisedPNL.DataPoints = append(realisedPNL.DataPoints, ChartPlot{ + Value: result.Events[i].PNL.GetRealisedPNL().PNL.InexactFloat64(), + UnixMilli: result.Events[i].PNL.GetRealisedPNL().Time.UnixMilli(), + }) + unrealisedPNL.DataPoints = append(unrealisedPNL.DataPoints, ChartPlot{ + Value: result.Events[i].PNL.GetUnrealisedPNL().PNL.InexactFloat64(), + UnixMilli: result.Events[i].PNL.GetUnrealisedPNL().Time.UnixMilli(), + }) + } + } + if len(unrealisedPNL.DataPoints) == 0 || len(realisedPNL.DataPoints) == 0 { + continue + } + resp = append(resp, unrealisedPNL, realisedPNL) + } + } + + } + return resp +} + // enhanceCandles will enhance candle data with order information allowing // report charts to have annotations to highlight buy and sell events func (d *Data) enhanceCandles() error { diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index 8b78bf833e6..b39b7e67728 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -310,9 +310,8 @@ func TestGenerateReport(t *testing.T) { MarketMovement: decimal.NewFromInt(1337), StrategyMovement: decimal.NewFromInt(1337), }, - CurrencyPairStatistics: nil, - WasAnyDataMissing: false, - FundingStatistics: nil, + WasAnyDataMissing: false, + FundingStatistics: nil, }, } d.OutputPath = tempDir diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index d751609b995..e115c68786c 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -41,6 +41,7 @@ type Data struct { UseDarkTheme bool USDTotalsChart []TotalsChart HoldingsOverTimeChart []TotalsChart + PNLOverTimeChart []TotalsChart Prettify PrettyNumbers } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index b4993755aa6..a5d742af786 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -779,6 +779,72 @@ // Apply the theme Highcharts.setOptions(Highcharts.theme); +

PNL Over Time

+
+ +
+ + {{ if eq $.Config.StrategySettings.DisableUSDTracking false }}

USD Totals

@@ -1638,12 +1704,12 @@ {{ if ne $ev.FillEvent nil }} {{$ev.FillEvent.GetTime}} - {{ $.Prettify.Decimal8 $ev.FillEvent.GetClosePrice}} {{if $asset.IsFutures}}{{$ev.PNL.GetCollateralCurrency}}{{else}}{{$pair.Quote}}{{end}} + {{ $.Prettify.Decimal8 $ev.FillEvent.GetClosePrice}} {{if $asset.IsFutures}}{{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}}{{else}}{{$pair.Quote}}{{end}} {{$ev.FillEvent.GetDirection}} {{$ev.FillEvent.GetReason}} {{ else if ne $ev.SignalEvent nil}} {{$ev.SignalEvent.GetTime}} - {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{if $asset.IsFutures}}{{$ev.PNL.GetCollateralCurrency}}{{else}}{{$pair.Quote}}{{end}} + {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{if $asset.IsFutures}}{{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}}{{else}}{{$pair.Quote}}{{end}} {{$ev.SignalEvent.GetDirection}} {{$ev.SignalEvent.GetReason}} {{ end }} @@ -1651,8 +1717,8 @@ {{if ne $ev.PNL nil }} {{ $.Prettify.Decimal8 $ev.PNL.GetExposure}} {{$pair.Base}}-{{$pair.Quote}} {{$ev.PNL.GetDirection}} - {{$ev.PNL.GetUnrealisedPNL.PNL}} {{$ev.PNL.GetCollateralCurrency}} - {{$ev.PNL.GetRealisedPNL.PNL}} {{$ev.PNL.GetCollateralCurrency}} + {{$ev.PNL.GetUnrealisedPNL.PNL}} {{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}} + {{$ev.PNL.GetRealisedPNL.PNL}}{{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}} {{else}} 0 {{$pair.Base}}-{{$pair.Quote}} N/A From 1e6ea3d191800c2d45f886f33cfa654af0988feb Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 22 Mar 2022 16:04:04 +1100 Subject: [PATCH 104/171] WHAT IS GOING ON WITH PNL --- .../config/examples/ftx-cash-carry.strat | 2 +- .../t2b2-api-candles-exchange-funding.strat | 2 +- .../ftxcashandcarry/ftxcashandcarry.go | 30 +++++++++++++++---- .../ftxcashandcarry/ftxcashandcarry_types.go | 3 ++ exchanges/ftx/ftx_wrapper.go | 3 +- exchanges/order/futures.go | 5 ++++ exchanges/order/futures_types.go | 2 ++ 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 5b47e4a9eff..b9ce220298a 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -10,7 +10,7 @@ "exchange-name": "ftx", "asset": "spot", "currency": "USD", - "initial-funds": "100000", + "initial-funds": "500", "transfer-fee": "0" } ], diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 8052db9b484..aa6752e43cd 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -17,7 +17,7 @@ "exchange-name": "ftx", "asset": "spot", "currency": "USDT", - "initial-funds": "10000", + "initial-funds": "10", "transfer-fee": "0" } ], diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index b983dc08cf8..0be00fa8b87 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -75,6 +75,12 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf futuresSignal.SetDirection(common.DoNothing) fp := v.futureSignal.Latest().GetClosePrice() sp := v.spotSignal.Latest().GetClosePrice() + hundred := decimal.NewFromInt(100) + diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(hundred) + futuresSignal.AppendReason(fmt.Sprintf("Difference %v", diffBetweenFuturesSpot)) + if pos != nil && pos[len(pos)-1].Status == order.Open { + futuresSignal.AppendReason(fmt.Sprintf("Unrealised PNL %v", pos[len(pos)-1].UnrealisedPNL)) + } switch { case len(pos) == 0: // check to see if order is appropriate to action @@ -95,21 +101,30 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf spotSignal.FillDependentEvent = &futuresSignal // only appending spotSignal as futuresSignal will be raised later response = append(response, &spotSignal) - case len(pos) > 0 && v.futureSignal.IsLastEvent(): + case len(pos) > 0 && + pos[len(pos)-1].Status == order.Open && + v.futureSignal.IsLastEvent(): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("Closing position on last event") response = append(response, &spotSignal, &futuresSignal) + case len(pos) > 0 && + pos[len(pos)-1].Status == order.Open && + pos[len(pos)-1].OpeningPrice.GreaterThan(futuresSignal.ClosePrice) && + s.alwaysCloseOnProfit: + futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.AppendReason(fmt.Sprintf("Closing position. Always close on profit. UPNL %v", pos[len(pos)-1].UnrealisedPNL)) + response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && - fp.Sub(sp).Div(sp).GreaterThan(s.closeShortDistancePercentage): + diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReason("Closing position after reaching close short distance percentage") + futuresSignal.AppendReason(fmt.Sprintf("Closing position. Threshold %v", s.closeShortDistancePercentage)) response = append(response, &spotSignal, &futuresSignal) case len(pos) > 0 && pos[len(pos)-1].Status == order.Closed && - fp.Sub(sp).Div(sp).GreaterThan(s.openShortDistancePercentage): + diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): futuresSignal.SetDirection(order.Short) futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) - futuresSignal.AppendReason("opening position after reaching open short distance percentage") + futuresSignal.AppendReason(fmt.Sprintf("Opening position. Threshold %v", s.openShortDistancePercentage)) response = append(response, &spotSignal, &futuresSignal) default: response = append(response, &spotSignal, &futuresSignal) @@ -184,5 +199,8 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro // SetDefaults not required for DCA func (s *Strategy) SetDefaults() { s.openShortDistancePercentage = decimal.NewFromInt(5) - s.closeShortDistancePercentage = decimal.NewFromInt(5) + s.closeShortDistancePercentage = decimal.Zero + // TODO set false + s.onlyCloseOnProfit = false + s.alwaysCloseOnProfit = false } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go index d2e72e29833..271f7421932 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go @@ -14,6 +14,7 @@ const ( exchangeName = "ftx" openShortDistancePercentageString = "openShortDistancePercentage" closeShortDistancePercentageString = "closeShortDistancePercentage" + onlyCloseOnProfitString = "onlyCloseOnProfit" ) var ( @@ -26,4 +27,6 @@ type Strategy struct { base.Strategy openShortDistancePercentage decimal.Decimal closeShortDistancePercentage decimal.Decimal + onlyCloseOnProfit bool + alwaysCloseOnProfit bool } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 5f5429f66f6..03186baf7fa 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1281,7 +1281,8 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) return nil, fmt.Errorf("%v %w", f.Name, order.ErrNilPNLCalculator) } result := &order.PNLResult{ - Time: pnl.Time, + Time: pnl.Time, + IsEvent: true, } var err error if pnl.CalculateOffline { diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 3f9bb7910cf..1fe852e6b55 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -428,6 +428,7 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro result := &PNLResult{ Time: t, Price: price, + Fee: decimal.Zero, } if p.currentDirection.IsLong() { diff := price.Sub(p.entryPrice) @@ -715,6 +716,7 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque } response := &PNLResult{ + IsEvent: true, Time: calc.Time, UnrealisedPNL: unrealisedPNL, RealisedPNLBeforeFees: realisedPNL, @@ -731,6 +733,9 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { + if !pnlHistory[i].IsEvent { + continue + } realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) totalFees = totalFees.Add(pnlHistory[i].Fee) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 5a258467953..7ad4691393e 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -272,6 +272,8 @@ type PNLResult struct { Direction Side Fee decimal.Decimal IsLiquidated bool + // Is event is supposed to show that something has happend and it isnt just tracking in time + IsEvent bool } // PositionStats is a basic holder From aad0f654d998fa5502bab21ea03d8bfd710372a8 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 23 Mar 2022 13:02:59 +1100 Subject: [PATCH 105/171] Fixes PNL calculation. Adds ability to skip om futures tracking --- backtester/data/data_test.go | 2 +- backtester/engine/setup.go | 2 +- .../eventhandlers/exchange/exchange_test.go | 6 ++--- engine/engine.go | 2 ++ engine/engine_types.go | 1 + engine/helpers.go | 1 + engine/order_manager.go | 11 ++++---- engine/order_manager_test.go | 16 +++++------ engine/order_manager_types.go | 1 + engine/rpcserver_test.go | 8 +++--- engine/websocketroutine_manager_test.go | 2 +- exchanges/ftx/ftx_wrapper.go | 2 +- exchanges/order/futures.go | 27 ++++++++++++++++--- exchanges/order/futures_types.go | 2 +- gctscript/wrappers/gct/gctwrapper_test.go | 2 +- main.go | 1 + 16 files changed, 56 insertions(+), 30 deletions(-) diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index 04fd9f2b23b..35d7e1eca91 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -87,7 +87,7 @@ func TestStream(t *testing.T) { // shut up coverage report f.GetOffset() f.SetOffset(1) - f.IsEvent() + f.IsOrder() f.Pair() f.GetExchange() f.GetInterval() diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index a467ac314cb..1734949298d 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -50,7 +50,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool var err error bt := New() bt.exchangeManager = engine.SetupExchangeManager() - bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false) + bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false, false) if err != nil { return nil, err } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 5b52612ea69..1c92dff9c65 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -166,7 +166,7 @@ func TestPlaceOrder(t *testing.T) { } em.Add(exch) bot.ExchangeManager = em - bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false) + bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false,false) if err != nil { t.Error(err) } @@ -224,7 +224,7 @@ func TestExecuteOrder(t *testing.T) { } em.Add(exch) bot.ExchangeManager = em - bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false) + bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, ,false,false) if err != nil { t.Error(err) } @@ -324,7 +324,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { em.Add(exch) bot.ExchangeManager = em - bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false) + bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false,false) if err != nil { t.Error(err) } diff --git a/engine/engine.go b/engine/engine.go index f857c361c26..ddff95933fe 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -274,6 +274,7 @@ func PrintSettings(s *Settings) { gctlog.Debugf(gctlog.Global, "\t Enable dispatcher: %v", s.EnableDispatcher) gctlog.Debugf(gctlog.Global, "\t Dispatch package max worker amount: %d", s.DispatchMaxWorkerAmount) gctlog.Debugf(gctlog.Global, "\t Dispatch package jobs limit: %d", s.DispatchJobsLimit) + gctlog.Debugf(gctlog.Global, "\t Futures PNL tracking: %v", s.EnableFuturesTracking) gctlog.Debugf(gctlog.Global, "- EXCHANGE SYNCER SETTINGS:\n") gctlog.Debugf(gctlog.Global, "\t Exchange sync continuously: %v\n", s.SyncContinuously) gctlog.Debugf(gctlog.Global, "\t Exchange sync workers count: %v\n", s.SyncWorkersCount) @@ -504,6 +505,7 @@ func (bot *Engine) Start() error { bot.ExchangeManager, bot.CommunicationsManager, &bot.ServicesWG, + bot.Settings.EnableFuturesTracking, bot.Settings.Verbose) if err != nil { gctlog.Errorf(gctlog.Global, "Order manager unable to setup: %s", err) diff --git a/engine/engine_types.go b/engine/engine_types.go index 3ecd6a869a0..af4d979f6af 100644 --- a/engine/engine_types.go +++ b/engine/engine_types.go @@ -38,6 +38,7 @@ type Settings struct { EnableWebsocketRoutine bool EnableCurrencyStateManager bool EventManagerDelay time.Duration + EnableFuturesTracking bool Verbose bool // Exchange syncer settings diff --git a/engine/helpers.go b/engine/helpers.go index f8282dba751..6711ca5fa06 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -138,6 +138,7 @@ func (bot *Engine) SetSubsystem(subSystemName string, enable bool) error { bot.ExchangeManager, bot.CommunicationsManager, &bot.ServicesWG, + true, bot.Settings.Verbose) if err != nil { return err diff --git a/engine/order_manager.go b/engine/order_manager.go index 443cb96ab14..5eca20de226 100644 --- a/engine/order_manager.go +++ b/engine/order_manager.go @@ -21,7 +21,7 @@ import ( ) // SetupOrderManager will boot up the OrderManager -func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager iCommsManager, wg *sync.WaitGroup, verbose bool) (*OrderManager, error) { +func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager iCommsManager, wg *sync.WaitGroup, enabledFuturesTracking, verbose bool) (*OrderManager, error) { if exchangeManager == nil { return nil, errNilExchangeManager } @@ -40,6 +40,7 @@ func SetupOrderManager(exchangeManager iExchangeManager, communicationsManager i commsManager: communicationsManager, wg: wg, futuresPositionController: order.SetupPositionController(), + trackFuturesPositions: enabledFuturesTracking, }, verbose: verbose, }, nil @@ -899,7 +900,7 @@ func (s *store) updateExisting(od *order.Detail) error { for x := range r { if r[x].ID == od.ID { r[x].UpdateOrderFromDetail(od) - if r[x].AssetType.IsFutures() { + if s.trackFuturesPositions && r[x].AssetType.IsFutures() { err := s.futuresPositionController.TrackNewOrder(r[x]) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { @@ -926,7 +927,7 @@ func (s *store) modifyExisting(id string, mod *order.Modify) error { for x := range r { if r[x].ID == id { r[x].UpdateOrderFromModify(mod) - if r[x].AssetType.IsFutures() { + if s.trackFuturesPositions && r[x].AssetType.IsFutures() { err := s.futuresPositionController.TrackNewOrder(r[x]) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { @@ -953,7 +954,7 @@ func (s *store) upsert(od *order.Detail) (resp *OrderUpsertResponse, err error) } s.m.Lock() defer s.m.Unlock() - if od.AssetType.IsFutures() { + if s.trackFuturesPositions && od.AssetType.IsFutures() { err = s.futuresPositionController.TrackNewOrder(od) if err != nil { if !errors.Is(err, order.ErrPositionClosed) { @@ -1057,7 +1058,7 @@ func (s *store) add(det *order.Detail) error { orders = append(orders, det) s.Orders[strings.ToLower(det.Exchange)] = orders - if det.AssetType.IsFutures() { + if s.trackFuturesPositions && det.AssetType.IsFutures() { err = s.futuresPositionController.TrackNewOrder(det) if err != nil { return err diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 9dc4ece790a..33f845fa43d 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -91,21 +91,21 @@ func (f omfExchange) ModifyOrder(ctx context.Context, action *order.Modify) (ord } func TestSetupOrderManager(t *testing.T) { - _, err := SetupOrderManager(nil, nil, nil, false) + _, err := SetupOrderManager(nil, nil, nil, false, false) if !errors.Is(err, errNilExchangeManager) { t.Errorf("error '%v', expected '%v'", err, errNilExchangeManager) } - _, err = SetupOrderManager(SetupExchangeManager(), nil, nil, false) + _, err = SetupOrderManager(SetupExchangeManager(), nil, nil, false, false) if !errors.Is(err, errNilCommunicationsManager) { t.Errorf("error '%v', expected '%v'", err, errNilCommunicationsManager) } - _, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, nil, false) + _, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, nil, false, false) if !errors.Is(err, errNilWaitGroup) { t.Errorf("error '%v', expected '%v'", err, errNilWaitGroup) } var wg sync.WaitGroup - _, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false) + _, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -118,7 +118,7 @@ func TestOrderManagerStart(t *testing.T) { t.Errorf("error '%v', expected '%v'", err, ErrNilSubsystem) } var wg sync.WaitGroup - m, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false) + m, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -139,7 +139,7 @@ func TestOrderManagerIsRunning(t *testing.T) { } var wg sync.WaitGroup - m, err := SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false) + m, err := SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -164,7 +164,7 @@ func TestOrderManagerStop(t *testing.T) { } var wg sync.WaitGroup - m, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false) + m, err = SetupOrderManager(SetupExchangeManager(), &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } @@ -207,7 +207,7 @@ func OrdersSetup(t *testing.T) *OrderManager { IBotExchange: exch, } em.Add(fakeExchange) - m, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false) + m, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } diff --git a/engine/order_manager_types.go b/engine/order_manager_types.go index b8166fd594e..b15e1cceddb 100644 --- a/engine/order_manager_types.go +++ b/engine/order_manager_types.go @@ -47,6 +47,7 @@ type store struct { exchangeManager iExchangeManager wg *sync.WaitGroup futuresPositionController *order.PositionController + trackFuturesPositions bool } // OrderManager processes and stores orders across enabled exchanges diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 37416228fe5..b28e3180fe0 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -1137,7 +1137,7 @@ func TestGetOrders(t *testing.T) { RequestFormat: ¤cy.PairFormat{Uppercase: true}} em.Add(exch) var wg sync.WaitGroup - om, err := SetupOrderManager(em, engerino.CommunicationsManager, &wg, false) + om, err := SetupOrderManager(em, engerino.CommunicationsManager, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("received '%v', expected '%v'", err, nil) } @@ -1245,7 +1245,7 @@ func TestGetOrder(t *testing.T) { RequestFormat: ¤cy.PairFormat{Uppercase: true}} em.Add(exch) var wg sync.WaitGroup - om, err := SetupOrderManager(em, engerino.CommunicationsManager, &wg, false) + om, err := SetupOrderManager(em, engerino.CommunicationsManager, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("received '%v', expected '%v'", err, nil) } @@ -1774,7 +1774,7 @@ func TestGetManagedOrders(t *testing.T) { RequestFormat: ¤cy.PairFormat{Uppercase: true}} em.Add(exch) var wg sync.WaitGroup - om, err := SetupOrderManager(em, engerino.CommunicationsManager, &wg, false) + om, err := SetupOrderManager(em, engerino.CommunicationsManager, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("received '%v', expected '%v'", err, nil) } @@ -2109,7 +2109,7 @@ func TestGetFuturesPositions(t *testing.T) { } em.Add(fakeExchange) var wg sync.WaitGroup - om, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false) + om, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("received '%v', expected '%v'", err, nil) } diff --git a/engine/websocketroutine_manager_test.go b/engine/websocketroutine_manager_test.go index 34cf56a9edc..e9255586067 100644 --- a/engine/websocketroutine_manager_test.go +++ b/engine/websocketroutine_manager_test.go @@ -131,7 +131,7 @@ func TestWebsocketRoutineManagerHandleData(t *testing.T) { exch.SetDefaults() em.Add(exch) - om, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false) + om, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 03186baf7fa..00fce4ced02 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1282,7 +1282,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) } result := &order.PNLResult{ Time: pnl.Time, - IsEvent: true, + IsOrder: true, } var err error if pnl.CalculateOffline { diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 1fe852e6b55..4a4ae6ad52e 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -12,6 +12,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller @@ -674,7 +675,16 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque } var previousPNL *PNLResult if len(calc.PNLHistory) > 0 { - previousPNL = &calc.PNLHistory[len(calc.PNLHistory)-1] + for i := len(calc.PNLHistory) - 1; i >= 0; i-- { + if calc.PNLHistory[i].Time.Equal(calc.Time) { + continue + } + if !calc.PNLHistory[i].IsOrder { + continue + } + previousPNL = &calc.PNLHistory[i] + break + } } var prevExposure decimal.Decimal if previousPNL != nil { @@ -716,7 +726,7 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque } response := &PNLResult{ - IsEvent: true, + IsOrder: true, Time: calc.Time, UnrealisedPNL: unrealisedPNL, RealisedPNLBeforeFees: realisedPNL, @@ -733,7 +743,7 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque func calculateRealisedPNL(pnlHistory []PNLResult) decimal.Decimal { var realisedPNL, totalFees decimal.Decimal for i := range pnlHistory { - if !pnlHistory[i].IsEvent { + if !pnlHistory[i].IsOrder { continue } realisedPNL = realisedPNL.Add(pnlHistory[i].RealisedPNLBeforeFees) @@ -750,7 +760,16 @@ func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, erro } for i := range pnlHistory { if entry.Time.Equal(pnlHistory[i].Time) { - pnlHistory[i] = *entry + pnlHistory[i].UnrealisedPNL = entry.UnrealisedPNL + pnlHistory[i].RealisedPNL = entry.RealisedPNL + pnlHistory[i].RealisedPNLBeforeFees = entry.RealisedPNLBeforeFees + pnlHistory[i].Exposure = entry.Exposure + pnlHistory[i].Direction = entry.Direction + pnlHistory[i].IsLiquidated = entry.IsLiquidated + pnlHistory[i].Price = entry.Price + if entry.IsOrder { + pnlHistory[i].IsOrder = true + } return pnlHistory, nil } } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 7ad4691393e..38ec569c86c 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -273,7 +273,7 @@ type PNLResult struct { Fee decimal.Decimal IsLiquidated bool // Is event is supposed to show that something has happend and it isnt just tracking in time - IsEvent bool + IsOrder bool } // PositionStats is a basic holder diff --git a/gctscript/wrappers/gct/gctwrapper_test.go b/gctscript/wrappers/gct/gctwrapper_test.go index 52f318f6386..4c0ff3b68ab 100644 --- a/gctscript/wrappers/gct/gctwrapper_test.go +++ b/gctscript/wrappers/gct/gctwrapper_test.go @@ -60,7 +60,7 @@ func TestMain(m *testing.M) { os.Exit(1) } - engine.Bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &engine.Bot.ServicesWG, false) + engine.Bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &engine.Bot.ServicesWG, false, false) if err != nil { log.Print(err) os.Exit(1) diff --git a/main.go b/main.go index 402c2a42ed9..956c29b40b7 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ func main() { flag.BoolVar(&settings.EnableDeprecatedRPC, "deprecatedrpc", true, "enables the deprecated RPC server") flag.BoolVar(&settings.EnableCommsRelayer, "enablecommsrelayer", true, "enables available communications relayer") flag.BoolVar(&settings.Verbose, "verbose", false, "increases logging verbosity for GoCryptoTrader") + flag.BoolVar(&settings.EnableFuturesTracking, "enablefuturestracking", true, "tracks futures orders PNL is supported by the exchange") flag.BoolVar(&settings.EnableExchangeSyncManager, "syncmanager", true, "enables to exchange sync manager") flag.BoolVar(&settings.EnableWebsocketRoutine, "websocketroutine", true, "enables the websocket routine for all loaded exchanges") flag.BoolVar(&settings.EnableCoinmarketcapAnalysis, "coinmarketcap", false, "overrides config and runs currency analysis") From 541c7b11149a2f129d0b61ca0f94aabfe375c134 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 23 Mar 2022 16:13:08 +1100 Subject: [PATCH 106/171] minor commit before merge --- .../eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go | 2 +- exchanges/ftx/ftx_wrapper.go | 2 +- exchanges/order/futures.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 0be00fa8b87..ba2a2bac91d 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -198,7 +198,7 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro // SetDefaults not required for DCA func (s *Strategy) SetDefaults() { - s.openShortDistancePercentage = decimal.NewFromInt(5) + s.openShortDistancePercentage = decimal.Zero s.closeShortDistancePercentage = decimal.Zero // TODO set false s.onlyCloseOnProfit = false diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 00fce4ced02..b2fb8703c39 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1299,7 +1299,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) if err != nil { return nil, err } - if info.Liquidating || info.Collateral == 0 { + if info.Liquidating || info.Collateral <= 0 { result.IsLiquidated = true return result, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, order.ErrPositionLiquidated) } diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 4a4ae6ad52e..ce4457d85cd 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -12,7 +12,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/log" ) // SetupPositionController creates a position controller From 1c07b83a55c0f82674091f0b54bcea10458b9ec4 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 24 Mar 2022 16:09:34 +1100 Subject: [PATCH 107/171] Adds basic liquidation to backtester --- backtester/engine/backtest.go | 22 +- .../eventhandlers/portfolio/portfolio.go | 71 ++++- .../portfolio/portfolio_types.go | 2 + .../eventhandlers/statistics/printresults.go | 5 + backtester/funding/collateral.go | 11 +- backtester/funding/funding.go | 6 +- exchanges/ftx/ftx_wrapper.go | 11 +- exchanges/order/futures.go | 258 +++++++++++------- exchanges/order/futures_types.go | 1 + exchanges/order/order_types.go | 2 + exchanges/order/orders.go | 10 +- 11 files changed, 257 insertions(+), 142 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 66ee644886d..6e1e8f4f97d 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" @@ -245,6 +246,13 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu return err } + pnl.Result.UnrealisedPNL = decimal.NewFromInt(-444 - ev.GetOffset()) + + pnl, err = bt.Portfolio.CheckLiquidationStatus(ev, cr, pnl) + if err != nil { + return err + } + return bt.Statistic.AddPNLForTime(pnl) } @@ -353,15 +361,15 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) return } - var receivingCurrency currency.Code - var receivingAsset asset.Item - receivingCurrency, receivingAsset, err = exch.GetCurrencyForRealisedPNL(ev.GetAssetType(), ev.Pair()) - if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return - } rPNL := pnl.GetRealisedPNL() if !rPNL.PNL.IsZero() { + var receivingCurrency currency.Code + var receivingAsset asset.Item + receivingCurrency, receivingAsset, err = exch.GetCurrencyForRealisedPNL(ev.GetAssetType(), ev.Pair()) + if err != nil { + log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return + } err = bt.Funding.RealisePNL(ev.GetExchange(), receivingAsset, receivingCurrency, rPNL.PNL) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "RealisePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 8671450283b..0e2058efaad 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -493,6 +493,19 @@ func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStat return settings.FuturesTracker.GetPositions(), nil } +// GetLatestPosition returns all futures positions for an event's exchange, asset, pair +func (p *Portfolio) GetLatestPosition(e common.EventHandler) (*gctorder.PositionStats, error) { + settings, err := p.getFuturesSettingsFromEvent(e) + if err != nil { + return nil, err + } + positions := settings.FuturesTracker.GetPositions() + if len(positions) == 0 { + return nil, fmt.Errorf("%w %v %v %v", gctorder.ErrPositionsNotLoadedForPair, e.GetExchange(), e.GetAssetType(), e.Pair()) + } + return &positions[len(positions)-1], nil +} + // UpdatePNL will analyse any futures orders that have been placed over the backtesting run // that are not closed and calculate their PNL func (p *Portfolio) UpdatePNL(e common.EventHandler, closePrice decimal.Decimal) error { @@ -555,39 +568,68 @@ func (p *Portfolio) TrackFuturesOrder(f fill.Event, fund funding.IFundReleaser) return nil, err } } - err = p.UpdatePNL(f, f.GetClosePrice()) + if err != nil { + return nil, fmt.Errorf("%v %v %v %w", f.GetExchange(), f.GetAssetType(), f.Pair(), err) + } - return p.GetLatestPNLForEvent(f) + pnl, err := p.GetLatestPNLForEvent(f) + if err != nil { + return nil, err + } + return pnl, nil } // GetLatestPNLForEvent takes in an event and returns the latest PNL data // if it exists func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, error) { - settings, err := p.getFuturesSettingsFromEvent(e) - if err != nil { - return nil, err - } response := &PNLSummary{ Exchange: e.GetExchange(), Item: e.GetAssetType(), Pair: e.Pair(), Offset: e.GetOffset(), } - - positions := settings.FuturesTracker.GetPositions() - if len(positions) == 0 { - return response, nil + position, err := p.GetLatestPosition(e) + if err != nil { + return nil, err } - pnlHistory := positions[len(positions)-1].PNLHistory + pnlHistory := position.PNLHistory if len(pnlHistory) == 0 { return response, nil } response.Result = pnlHistory[len(pnlHistory)-1] - response.CollateralCurrency = positions[0].CollateralCurrency + response.CollateralCurrency = position.CollateralCurrency return response, nil } +// CheckLiquidationStatus checks funding against position +// and liquidates and removes funding if position unable to continue +func (p *Portfolio) CheckLiquidationStatus(e common.DataEventHandler, collateralReleaser funding.ICollateralReleaser, pnl *PNLSummary) (*PNLSummary, error) { + settings, err := p.getFuturesSettingsFromEvent(e) + if err != nil { + return nil, err + } + + availableFunds := collateralReleaser.AvailableFunds() + position, err := p.GetLatestPosition(e) + if err != nil { + return nil, err + } + if !position.Status.IsInactive() && + pnl.Result.UnrealisedPNL.IsNegative() && + pnl.Result.UnrealisedPNL.Abs().GreaterThan(availableFunds) { + // rudimentary liquidation processing until wrapper level implementation + err = settings.FuturesTracker.Liquidate(e.GetClosePrice(), e.GetTime()) + if err != nil { + return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) + } + collateralReleaser.Liquidate() + e.AppendReason(fmt.Sprintf("Liquidated, funds '%v', collateral '%v'", pnl.Result.UnrealisedPNL, availableFunds)) + } + + return p.GetLatestPNLForEvent(e) +} + func (p *Portfolio) getFuturesSettingsFromEvent(e common.EventHandler) (*Settings, error) { if e == nil { return nil, common.ErrNilEvent @@ -692,3 +734,8 @@ func (p PNLSummary) GetCollateralCurrency() currency.Code { func (p PNLSummary) GetDirection() gctorder.Side { return p.Result.Direction } + +// GetPositionStatus returns the position status +func (p PNLSummary) GetPositionStatus() gctorder.Status { + return p.Result.Status +} diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 76d5edb8cac..a2ab7fc5629 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -61,6 +61,7 @@ type Handler interface { UpdatePNL(common.EventHandler, decimal.Decimal) error GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary + CheckLiquidationStatus(common.DataEventHandler, funding.ICollateralReleaser, *PNLSummary) (*PNLSummary, error) Reset() } @@ -100,6 +101,7 @@ type IPNL interface { GetRealisedPNL() BasicPNLResult GetCollateralCurrency() currency.Code GetDirection() gctorder.Side + GetPositionStatus() gctorder.Status } // BasicPNLResult holds the time and the pnl diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 51f4265d747..e03dc9b44e6 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -85,6 +86,10 @@ func (s *Statistic) PrintAllEventsChronologically() { if direction == common.DoNothing { colour = common.ColourDarkGrey } + if currencyStatistic.Events[i].PNL != nil && + currencyStatistic.Events[i].PNL.GetPositionStatus() == gctorder.Liquidated { + colour = common.ColourError + } msg := fmt.Sprintf(colour+ "%v %v%v%v| Price: $%v\tDirection: %v", currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 50f947a4f20..17fee123f80 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -7,7 +7,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) // collateral related errors @@ -19,7 +19,7 @@ var ( ) // CanPlaceOrder checks if there is any collateral to spare -func (c *Collateral) CanPlaceOrder(_ order.Side) bool { +func (c *Collateral) CanPlaceOrder(_ gctorder.Side) bool { return c.Collateral.CanPlaceOrder() } @@ -63,7 +63,7 @@ func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { } // UpdateContracts adds or subtracts contracts based on order direction -func (c *Collateral) UpdateContracts(s order.Side, amount decimal.Decimal) error { +func (c *Collateral) UpdateContracts(s gctorder.Side, amount decimal.Decimal) error { switch { case c.currentDirection == nil: c.currentDirection = &s @@ -105,9 +105,9 @@ func (c *Collateral) PairReleaser() (IPairReleaser, error) { } // Reserve reserves or releases collateral based on order side -func (c *Collateral) Reserve(amount decimal.Decimal, side order.Side) error { +func (c *Collateral) Reserve(amount decimal.Decimal, side gctorder.Side) error { switch side { - case order.Long, order.Short: + case gctorder.Long, gctorder.Short: return c.Collateral.Reserve(amount) case common.ClosePosition: return c.Collateral.Release(amount, amount) @@ -137,6 +137,7 @@ func (c *Collateral) FundReleaser() IFundReleaser { func (c *Collateral) Liquidate() { c.Collateral.available = decimal.Zero c.Contract.available = decimal.Zero + c.currentDirection = nil } // CurrentHoldings returns available contract holdings diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 851b26e65ce..e1fd36dfb3d 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -18,7 +18,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/log" ) var ( @@ -497,7 +496,7 @@ func (f *FundManager) getFundingForEAC(exch string, a asset.Item, c currency.Cod func (f *FundManager) LiquidateByCollateral(c currency.Code) error { found := false for i := range f.items { - if f.items[i].currency == c && !f.items[i].isCollateral && f.items[i].asset.IsFutures() { + if f.items[i].currency == c && f.items[i].isCollateral && f.items[i].asset.IsFutures() { f.items[i].available = decimal.Zero f.items[i].reserved = decimal.Zero found = true @@ -509,7 +508,7 @@ func (f *FundManager) LiquidateByCollateral(c currency.Code) error { for i := range f.items { if f.items[i].pairedWith != nil && f.items[i].pairedWith.currency == c && - f.items[i].asset == asset.Futures { + f.items[i].asset.IsFutures() { f.items[i].available = decimal.Zero f.items[i].reserved = decimal.Zero } @@ -620,7 +619,6 @@ func (f *FundManager) HasFutures() bool { // RealisePNL adds the realised PNL to a receiving exchange asset pair func (f *FundManager) RealisePNL(receivingExchange string, receivingAsset asset.Item, receivingCurrency currency.Code, realisedPNL decimal.Decimal) error { - log.Debugf(log.Global, "\n%v %v %v %v WOW\n", receivingExchange, receivingAsset, receivingCurrency, realisedPNL) for i := range f.items { if f.items[i].exchange == receivingExchange && f.items[i].asset == receivingAsset && diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index b2fb8703c39..17fed7e06e5 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -1288,10 +1288,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) if pnl.CalculateOffline { // PNLCalculator matches FTX's pnl calculation method calc := order.PNLCalculator{} - result, err = calc.CalculatePNL(ctx, pnl) - if err != nil { - return nil, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, err) - } + return calc.CalculatePNL(ctx, pnl) } ep := pnl.EntryPrice.InexactFloat64() @@ -1317,11 +1314,7 @@ func (f *FTX) CalculatePNL(ctx context.Context, pnl *order.PNLCalculatorRequest) } // order no longer active, use offline calculation calc := order.PNLCalculator{} - result, err = calc.CalculatePNL(ctx, pnl) - if err != nil { - return nil, fmt.Errorf("%s %s %w", f.Name, f.API.Credentials.Subaccount, err) - } - return result, nil + return calc.CalculatePNL(ctx, pnl) } // ScaleCollateral takes your totals and scales them according to FTX's rules diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index ce4457d85cd..b9a5b02e0b6 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -174,6 +174,83 @@ func (c *PositionController) UpdateOpenPositionUnrealisedPNL(exch string, item a return latestPos.unrealisedPNL, nil } +// SetupMultiPositionTracker creates a futures order tracker for a specific exchange +func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPositionTracker, error) { + if setup == nil { + return nil, errNilSetup + } + if setup.Exchange == "" { + return nil, errExchangeNameEmpty + } + if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { + return nil, ErrNotFuturesAsset + } + if setup.Pair.IsEmpty() { + return nil, ErrPairIsEmpty + } + if setup.Underlying.IsEmpty() { + return nil, errEmptyUnderlying + } + if setup.ExchangePNLCalculation == nil && setup.UseExchangePNLCalculation { + return nil, errMissingPNLCalculationFunctions + } + return &MultiPositionTracker{ + exchange: strings.ToLower(setup.Exchange), + asset: setup.Asset, + pair: setup.Pair, + underlying: setup.Underlying, + offlinePNLCalculation: setup.OfflineCalculation, + orderPositions: make(map[string]*PositionTracker), + useExchangePNLCalculations: setup.UseExchangePNLCalculation, + exchangePNLCalculation: setup.ExchangePNLCalculation, + collateralCurrency: setup.CollateralCurrency, + }, nil +} + +// SetupPositionTracker creates a new position tracker to track n futures orders +// until the position(s) are closed +func (m *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { + if m == nil { + return nil, fmt.Errorf("multi-position tracker %w", common.ErrNilPointer) + } + if m.exchange == "" { + return nil, errExchangeNameEmpty + } + if setup == nil { + return nil, errNilSetup + } + if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { + return nil, ErrNotFuturesAsset + } + if setup.Pair.IsEmpty() { + return nil, ErrPairIsEmpty + } + + resp := &PositionTracker{ + exchange: strings.ToLower(m.exchange), + asset: setup.Asset, + contractPair: setup.Pair, + underlyingAsset: setup.Underlying, + status: Open, + entryPrice: setup.EntryPrice, + currentDirection: setup.Side, + openingDirection: setup.Side, + useExchangePNLCalculation: setup.UseExchangePNLCalculation, + collateralCurrency: setup.CollateralCurrency, + offlinePNLCalculation: m.offlinePNLCalculation, + } + if !setup.UseExchangePNLCalculation { + // use position tracker's pnl calculation by default + resp.PNLCalculation = &PNLCalculator{} + } else { + if m.exchangePNLCalculation == nil { + return nil, ErrNilPNLCalculator + } + resp.PNLCalculation = m.exchangePNLCalculation + } + return resp, nil +} + // UpdateOpenPositionUnrealisedPNL updates the pnl for the latest open position // based on the last price and the time func (m *MultiPositionTracker) UpdateOpenPositionUnrealisedPNL(last float64, updated time.Time) (decimal.Decimal, error) { @@ -184,7 +261,7 @@ func (m *MultiPositionTracker) UpdateOpenPositionUnrealisedPNL(last float64, upd return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionsNotLoadedForPair) } latestPos := pos[len(pos)-1] - if latestPos.status != Open { + if latestPos.status.IsInactive() { return decimal.Zero, fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionClosed) } err := latestPos.TrackPNLByTime(updated, last) @@ -236,84 +313,51 @@ func (c *PositionController) ClearPositionsForExchange(exch string, item asset.I return nil } -// SetupMultiPositionTracker creates a futures order tracker for a specific exchange -func SetupMultiPositionTracker(setup *MultiPositionTrackerSetup) (*MultiPositionTracker, error) { - if setup == nil { - return nil, errNilSetup - } - if setup.Exchange == "" { - return nil, errExchangeNameEmpty - } - if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { - return nil, ErrNotFuturesAsset - } - if setup.Pair.IsEmpty() { - return nil, ErrPairIsEmpty - } - if setup.Underlying.IsEmpty() { - return nil, errEmptyUnderlying - } - if setup.ExchangePNLCalculation == nil && setup.UseExchangePNLCalculation { - return nil, errMissingPNLCalculationFunctions - } - return &MultiPositionTracker{ - exchange: strings.ToLower(setup.Exchange), - asset: setup.Asset, - pair: setup.Pair, - underlying: setup.Underlying, - offlinePNLCalculation: setup.OfflineCalculation, - orderPositions: make(map[string]*PositionTracker), - useExchangePNLCalculations: setup.UseExchangePNLCalculation, - exchangePNLCalculation: setup.ExchangePNLCalculation, - collateralCurrency: setup.CollateralCurrency, - }, nil -} - // GetPositions returns all positions -func (e *MultiPositionTracker) GetPositions() []PositionStats { - if e == nil { +func (m *MultiPositionTracker) GetPositions() []PositionStats { + if m == nil { return nil } - e.m.Lock() - defer e.m.Unlock() + m.m.Lock() + defer m.m.Unlock() var resp []PositionStats - for i := range e.positions { - resp = append(resp, e.positions[i].GetStats()) + for i := range m.positions { + resp = append(resp, m.positions[i].GetStats()) } return resp } // TrackNewOrder upserts an order to the tracker and updates position // status and exposure. PNL is calculated separately as it requires mark prices -func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { - if e == nil { +func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { + if m == nil { return fmt.Errorf("multi-position tracker %w", common.ErrNilPointer) } if d == nil { return ErrSubmissionIsNil } - e.m.Lock() - defer e.m.Unlock() - if d.AssetType != e.asset { + m.m.Lock() + defer m.m.Unlock() + if d.AssetType != m.asset { return errAssetMismatch } - if tracker, ok := e.orderPositions[d.ID]; ok { + if tracker, ok := m.orderPositions[d.ID]; ok { // this has already been associated // update the tracker return tracker.TrackNewOrder(d) } - if len(e.positions) > 0 { - for i := range e.positions { - if e.positions[i].status == Open && i != len(e.positions)-1 { - return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, e.positions[i], i, len(e.positions)-1) + if len(m.positions) > 0 { + for i := range m.positions { + if m.positions[i].status == Open && i != len(m.positions)-1 { + return fmt.Errorf("%w %v at position %v/%v", errPositionDiscrepancy, m.positions[i], i, len(m.positions)-1) } } - if e.positions[len(e.positions)-1].status == Open { - err := e.positions[len(e.positions)-1].TrackNewOrder(d) + if m.positions[len(m.positions)-1].status == Open { + err := m.positions[len(m.positions)-1].TrackNewOrder(d) if err != nil && !errors.Is(err, ErrPositionClosed) { return err } - e.orderPositions[d.ID] = e.positions[len(e.positions)-1] + m.orderPositions[d.ID] = m.positions[len(m.positions)-1] return nil } } @@ -323,64 +367,34 @@ func (e *MultiPositionTracker) TrackNewOrder(d *Detail) error { Underlying: d.Pair.Base, Asset: d.AssetType, Side: d.Side, - UseExchangePNLCalculation: e.useExchangePNLCalculations, - CollateralCurrency: e.collateralCurrency, + UseExchangePNLCalculation: m.useExchangePNLCalculations, + CollateralCurrency: m.collateralCurrency, } - tracker, err := e.SetupPositionTracker(setup) + tracker, err := m.SetupPositionTracker(setup) if err != nil { return err } - e.positions = append(e.positions, tracker) + m.positions = append(m.positions, tracker) err = tracker.TrackNewOrder(d) if err != nil { return err } - e.orderPositions[d.ID] = tracker + m.orderPositions[d.ID] = tracker return nil } -// SetupPositionTracker creates a new position tracker to track n futures orders -// until the position(s) are closed -func (e *MultiPositionTracker) SetupPositionTracker(setup *PositionTrackerSetup) (*PositionTracker, error) { - if e == nil { - return nil, fmt.Errorf("multi-position tracker %w", common.ErrNilPointer) - } - if e.exchange == "" { - return nil, errExchangeNameEmpty - } - if setup == nil { - return nil, errNilSetup - } - if !setup.Asset.IsValid() || !setup.Asset.IsFutures() { - return nil, ErrNotFuturesAsset - } - if setup.Pair.IsEmpty() { - return nil, ErrPairIsEmpty - } - - resp := &PositionTracker{ - exchange: strings.ToLower(e.exchange), - asset: setup.Asset, - contractPair: setup.Pair, - underlyingAsset: setup.Underlying, - status: Open, - entryPrice: setup.EntryPrice, - currentDirection: setup.Side, - openingDirection: setup.Side, - useExchangePNLCalculation: setup.UseExchangePNLCalculation, - collateralCurrency: setup.CollateralCurrency, - offlinePNLCalculation: e.offlinePNLCalculation, +// Liquidate will update the latest open position's +// to reflect its liquidated status +func (m *MultiPositionTracker) Liquidate(price decimal.Decimal, t time.Time) error { + if m == nil { + return fmt.Errorf("multi-position tracker %w", common.ErrNilPointer) } - if !setup.UseExchangePNLCalculation { - // use position tracker's pnl calculation by default - resp.PNLCalculation = &PNLCalculator{} - } else { - if e.exchangePNLCalculation == nil { - return nil, ErrNilPNLCalculator - } - resp.PNLCalculation = e.exchangePNLCalculation + m.m.Lock() + defer m.m.Unlock() + if len(m.positions) == 0 { + return fmt.Errorf("%v %v %v %w", m.exchange, m.asset, m.pair, ErrPositionsNotLoadedForPair) } - return resp, nil + return m.positions[len(m.positions)-1].Liquidate(price, t) } // GetStats returns a summary of a future position @@ -426,9 +440,10 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro }() price := decimal.NewFromFloat(currentPrice) result := &PNLResult{ - Time: t, - Price: price, - Fee: decimal.Zero, + Time: t, + Price: price, + Fee: decimal.Zero, + Status: p.status, } if p.currentDirection.IsLong() { diff := price.Sub(p.entryPrice) @@ -462,6 +477,38 @@ func (p *PositionTracker) GetRealisedPNL() decimal.Decimal { return calculateRealisedPNL(p.pnlHistory) } +// Liquidate will update the positions stats to reflect its liquidation +func (p *PositionTracker) Liquidate(price decimal.Decimal, t time.Time) error { + if p == nil { + return fmt.Errorf("position tracker %w", common.ErrNilPointer) + } + p.m.Lock() + defer p.m.Unlock() + if p.status.IsInactive() { + return fmt.Errorf("%w. Status: %v", ErrPositionClosed, p.status) + } + p.status = Liquidated + p.currentDirection = SideNA + p.exposure = decimal.Zero + p.realisedPNL = decimal.Zero + p.unrealisedPNL = decimal.Zero + _, err := upsertPNLEntry(p.pnlHistory, &PNLResult{ + Time: t, + UnrealisedPNL: decimal.Zero, + RealisedPNLBeforeFees: decimal.Zero, + RealisedPNL: decimal.Zero, + Price: price, + Exposure: decimal.Zero, + Direction: SideNA, + Fee: decimal.Zero, + IsLiquidated: true, + IsOrder: true, + Status: p.status, + }) + + return err +} + // GetLatestPNLSnapshot takes the latest pnl history value // and returns it func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { @@ -479,7 +526,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { } p.m.Lock() defer p.m.Unlock() - if p.status == Closed { + if p.status.IsInactive() { return ErrPositionClosed } if d == nil { @@ -588,6 +635,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if err != nil { return err } + result.Status = p.status p.pnlHistory, err = upsertPNLEntry(cal.PNLHistory, result) if err != nil { return err @@ -620,6 +668,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { result.RealisedPNLBeforeFees = decimal.Zero p.status = Closed } + result.Status = p.status p.pnlHistory, err = upsertPNLEntry(p.pnlHistory, result) if err != nil { return err @@ -632,7 +681,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { case shortSide.GreaterThan(longSide): p.currentDirection = Short default: - p.currentDirection = UnknownSide + p.currentDirection = SideNA } if p.currentDirection.IsLong() { @@ -734,6 +783,9 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque Fee: calc.Fee, Direction: calc.CurrentDirection, } + if len(calc.PNLHistory) > 3 { + response.UnrealisedPNL = decimal.NewFromInt(-9999999) + } return response, nil } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index 38ec569c86c..c5aad25ccad 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -263,6 +263,7 @@ type PNLCalculatorRequest struct { // PNLResult stores a PNL result from a point in time type PNLResult struct { + Status Status Time time.Time UnrealisedPNL decimal.Decimal RealisedPNLBeforeFees decimal.Decimal diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 5ef124dbe90..e39207073ed 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -259,6 +259,7 @@ const ( AutoDeleverage Status = "ADL" Closed Status = "CLOSED" Pending Status = "PENDING" + Liquidated Status = "LIQUIDATED" ) // Type enforces a standard for order types across the code base @@ -297,6 +298,7 @@ const ( UnknownSide Side = "UNKNOWN" Long Side = "LONG" Short Side = "SHORT" + SideNA Side = "N/A" ) // ByPrice used for sorting orders by price diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index 39aaf14f2c9..b4e9f90a576 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -432,8 +432,7 @@ func (d *Detail) IsInactive() bool { if d.Amount <= 0 || d.Amount <= d.ExecutedAmount { return true } - return d.Status == Filled || d.Status == Cancelled || d.Status == InsufficientBalance || d.Status == MarketUnavailable || - d.Status == Rejected || d.Status == PartiallyCancelled || d.Status == Expired || d.Status == Closed + return d.Status.IsInactive() } // GenerateInternalOrderID sets a new V4 order ID or a V5 order ID if @@ -946,3 +945,10 @@ func (m *Modify) Validate(opt ...validate.Checker) error { } return nil } + +// IsInactive returns true if order is closed. Only for explicit closed status +// eg insufficient_balance is likely closed, but not concrete enough to include +func (s Status) IsInactive() bool { + return s == Filled || s == Cancelled || s == InsufficientBalance || s == MarketUnavailable || + s == Rejected || s == PartiallyCancelled || s == Expired || s == Closed || s == Liquidated +} From 0759fe1a9020f87c9960eb45990d8b0b940a2cfb Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 25 Mar 2022 15:01:45 +1100 Subject: [PATCH 108/171] Changes liquidation to order based --- backtester/engine/backtest.go | 31 ++++++- backtester/eventhandlers/exchange/exchange.go | 8 +- .../eventhandlers/portfolio/portfolio.go | 91 +++++++++++++++---- .../portfolio/portfolio_types.go | 3 +- .../eventhandlers/statistics/printresults.go | 13 +-- backtester/funding/collateral.go | 2 + backtester/funding/funding_types.go | 1 + backtester/funding/pair.go | 9 ++ backtester/main.go | 2 +- exchanges/order/futures.go | 5 +- 10 files changed, 133 insertions(+), 32 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 6e1e8f4f97d..ccbf4e5ae4b 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -181,8 +181,15 @@ func (bt *BackTest) processSimultaneousDataEvents() error { return err } err = bt.updateStatsForDataEvent(latestData, funds.FundReleaser()) - if err != nil && err == statistics.ErrAlreadyProcessed { - continue + if err != nil { + switch { + case errors.Is(err, statistics.ErrAlreadyProcessed): + continue + case errors.Is(err, gctorder.ErrPositionLiquidated): + return nil + default: + log.Error(common.SubLoggers[common.Backtester], err) + } } dataEvents = append(dataEvents, dataHandler) } @@ -248,8 +255,24 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu pnl.Result.UnrealisedPNL = decimal.NewFromInt(-444 - ev.GetOffset()) - pnl, err = bt.Portfolio.CheckLiquidationStatus(ev, cr, pnl) + err = bt.Portfolio.CheckLiquidationStatus(ev, cr, pnl) if err != nil { + if errors.Is(err, gctorder.ErrPositionLiquidated) { + // trigger closure of everything + orders, err := bt.Portfolio.CreateLiquidationOrders(ev) + if err != nil { + return err + } + for i := range orders { + bt.EventQueue.AppendEvent(orders[i]) + } + + pnl.Result.IsLiquidated = true + pnlErr := bt.Statistic.AddPNLForTime(pnl) + if pnlErr != nil { + return pnlErr + } + } return err } @@ -337,7 +360,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) } fde := ev.GetFillDependentEvent() - if !fde.IsNil() { + if fde != nil && !fde.IsNil() { // some events can only be triggered on a successful fill event fde.SetOffset(ev.GetOffset()) err = bt.Statistic.SetEventForOffset(fde) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index ae278026d0e..937a8de74a6 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -136,9 +136,11 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.ExchangeFee = calculateExchangeFee(adjustedPrice, limitReducedAmount, cs.ExchangeFee) orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) - err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) - if err != nil { - return f, err + if !o.IsClosingPosition() { + err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) + if err != nil { + return f, err + } } ords := orderManager.GetOrdersSnapshot("") diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 0e2058efaad..4c8cb24db29 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -604,30 +604,89 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er // CheckLiquidationStatus checks funding against position // and liquidates and removes funding if position unable to continue -func (p *Portfolio) CheckLiquidationStatus(e common.DataEventHandler, collateralReleaser funding.ICollateralReleaser, pnl *PNLSummary) (*PNLSummary, error) { - settings, err := p.getFuturesSettingsFromEvent(e) - if err != nil { - return nil, err - } - - availableFunds := collateralReleaser.AvailableFunds() +func (p *Portfolio) CheckLiquidationStatus(e common.DataEventHandler, collateralReader funding.ICollateralReader, pnl *PNLSummary) error { + availableFunds := collateralReader.AvailableFunds() position, err := p.GetLatestPosition(e) if err != nil { - return nil, err + return err } if !position.Status.IsInactive() && pnl.Result.UnrealisedPNL.IsNegative() && pnl.Result.UnrealisedPNL.Abs().GreaterThan(availableFunds) { - // rudimentary liquidation processing until wrapper level implementation - err = settings.FuturesTracker.Liquidate(e.GetClosePrice(), e.GetTime()) - if err != nil { - return nil, fmt.Errorf("%v %v %v %w", e.GetExchange(), e.GetAssetType(), e.Pair(), err) - } - collateralReleaser.Liquidate() - e.AppendReason(fmt.Sprintf("Liquidated, funds '%v', collateral '%v'", pnl.Result.UnrealisedPNL, availableFunds)) + return gctorder.ErrPositionLiquidated } - return p.GetLatestPNLForEvent(e) + return nil +} + +func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler) ([]*order.Order, error) { + var closingOrders []*order.Order + for exch, assetMap := range p.exchangeAssetPairSettings { + for item, pairMap := range assetMap { + for pair, settings := range pairMap { + switch { + case item.IsFutures(): + positions := settings.FuturesTracker.GetPositions() + if len(positions) == 0 { + continue + } + pos := positions[len(positions)-1] + if !pos.Exposure.IsPositive() { + continue + } + direction := gctorder.Short + if pos.LatestDirection == gctorder.Short { + direction = gctorder.Long + } + closingOrders = append(closingOrders, &order.Order{ + Base: event.Base{ + Offset: ev.GetOffset(), + Exchange: pos.Exchange, + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: pos.Pair, + UnderlyingPair: pos.Pair, + AssetType: pos.Asset, + Reason: "LIQUIDATED", + }, + ID: "", + Direction: direction, + Status: gctorder.Liquidated, + Price: ev.GetClosePrice(), + Amount: pos.Exposure, + OrderType: gctorder.Market, + ClosingPosition: true, + }) + + case item == asset.Spot: + snappy := settings.ComplianceManager.GetLatestSnapshot() + if len(snappy.Orders) == 0 { + continue + } + what := snappy.Orders[len(snappy.Orders)-1] + closingOrders = append(closingOrders, &order.Order{ + Base: event.Base{ + Offset: ev.GetOffset(), + Exchange: exch, + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: pair, + AssetType: item, + Reason: "LIQUIDATED", + }, + Direction: gctorder.Sell, + Status: gctorder.Liquidated, + Price: what.ClosePrice, + Amount: decimal.NewFromFloat(what.Order.Amount), + OrderType: gctorder.Market, + AllocatedSize: decimal.NewFromFloat(what.Order.Amount), + ClosingPosition: true, + }) + } + } + } + } + return closingOrders, nil } func (p *Portfolio) getFuturesSettingsFromEvent(e common.EventHandler) (*Settings, error) { diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index a2ab7fc5629..0f22c99f130 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -61,7 +61,8 @@ type Handler interface { UpdatePNL(common.EventHandler, decimal.Decimal) error GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary - CheckLiquidationStatus(common.DataEventHandler, funding.ICollateralReleaser, *PNLSummary) (*PNLSummary, error) + CheckLiquidationStatus(common.DataEventHandler, funding.ICollateralReader, *PNLSummary) error + CreateLiquidationOrders(common.DataEventHandler) ([]*order.Order, error) Reset() } diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index e03dc9b44e6..9074173b219 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -69,6 +69,7 @@ func (s *Statistic) PrintAllEventsChronologically() { var results []eventOutputHolder log.Info(common.SubLoggers[common.Statistics], common.ColourH1+"------------------Events-------------------------------------"+common.ColourDefault) var errs gctcommon.Errors + colour := common.ColourDefault for exch, x := range s.ExchangeAssetPairStatistics { for a, y := range x { for pair, currencyStatistic := range y { @@ -82,14 +83,9 @@ func (s *Statistic) PrintAllEventsChronologically() { direction == common.DoNothing || direction == common.TransferredFunds || direction == "" { - colour := common.ColourProblem if direction == common.DoNothing { colour = common.ColourDarkGrey } - if currencyStatistic.Events[i].PNL != nil && - currencyStatistic.Events[i].PNL.GetPositionStatus() == gctorder.Liquidated { - colour = common.ColourError - } msg := fmt.Sprintf(colour+ "%v %v%v%v| Price: $%v\tDirection: %v", currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), @@ -102,7 +98,12 @@ func (s *Statistic) PrintAllEventsChronologically() { msg = msg + common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) } else { - msg := fmt.Sprintf(common.ColourSuccess+ + // successful order! + colour = common.ColourSuccess + if currencyStatistic.Events[i].PNL != nil && currencyStatistic.Events[i].PNL.GetPositionStatus() == gctorder.Liquidated { + colour = common.ColourError + } + msg := fmt.Sprintf(colour+ "%v %v%v%v| Price: $%v\tAmount: %v\tFee: $%v\tTotal: $%v\tDirection %v", currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), fSIL(exch, limit12), diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 17fee123f80..0a23ea9c276 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -136,7 +136,9 @@ func (c *Collateral) FundReleaser() IFundReleaser { // Liquidate kills your funds and future func (c *Collateral) Liquidate() { c.Collateral.available = decimal.Zero + c.Collateral.reserved = decimal.Zero c.Contract.available = decimal.Zero + c.Contract.reserved = decimal.Zero c.currentDirection = nil } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 92449e24a3d..cad9716a632 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -108,6 +108,7 @@ type IPairReleaser interface { IPairReader IncreaseAvailable(decimal.Decimal, order.Side) Release(decimal.Decimal, decimal.Decimal, order.Side) error + Liquidate() } type ICollateralReleaser interface { diff --git a/backtester/funding/pair.go b/backtester/funding/pair.go index e926b9bc432..913d16a766a 100644 --- a/backtester/funding/pair.go +++ b/backtester/funding/pair.go @@ -134,3 +134,12 @@ func (p *Pair) CollateralReleaser() (ICollateralReleaser, error) { func (p *Pair) FundReleaser() IFundReleaser { return p } + +// Liquidate basic liquidation response to remove +// all asset value +func (p *Pair) Liquidate() { + p.Base.available = decimal.Zero + p.Base.reserved = decimal.Zero + p.Quote.available = decimal.Zero + p.Quote.reserved = decimal.Zero +} diff --git a/backtester/main.go b/backtester/main.go index 6178abf70ca..082d7d47be3 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -71,7 +71,7 @@ func main() { flag.BoolVar( &colourOutput, "colouroutput", - false, + true, "if enabled, will print in colours, if your terminal supports \033[38;5;99m[colours like this]\u001b[0m") flag.BoolVar( &logSubHeader, diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b9a5b02e0b6..9b385bd2936 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -816,11 +816,14 @@ func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, erro pnlHistory[i].RealisedPNLBeforeFees = entry.RealisedPNLBeforeFees pnlHistory[i].Exposure = entry.Exposure pnlHistory[i].Direction = entry.Direction - pnlHistory[i].IsLiquidated = entry.IsLiquidated pnlHistory[i].Price = entry.Price + pnlHistory[i].Status = entry.Status if entry.IsOrder { pnlHistory[i].IsOrder = true } + if entry.IsLiquidated { + pnlHistory[i].IsLiquidated = true + } return pnlHistory, nil } } From 8c19c105ae5c539de7cd2a65eca0738f76068ba6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 28 Mar 2022 17:01:46 +1100 Subject: [PATCH 109/171] Liquidationnnnnn --- backtester/engine/backtest.go | 3 +- backtester/eventhandlers/exchange/exchange.go | 27 +++++++- .../eventhandlers/portfolio/portfolio.go | 67 ++++++++++--------- .../portfolio/portfolio_types.go | 2 +- .../ftxcashandcarry/ftxcashandcarry.go | 8 +++ backtester/eventtypes/order/order.go | 6 ++ backtester/eventtypes/order/order_types.go | 26 +++---- backtester/funding/funding.go | 42 ++++++------ backtester/funding/funding_types.go | 5 +- 9 files changed, 117 insertions(+), 69 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index ccbf4e5ae4b..db1115dc356 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -259,14 +259,13 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if err != nil { if errors.Is(err, gctorder.ErrPositionLiquidated) { // trigger closure of everything - orders, err := bt.Portfolio.CreateLiquidationOrders(ev) + orders, err := bt.Portfolio.CreateLiquidationOrders(ev, bt.Funding) if err != nil { return err } for i := range orders { bt.EventQueue.AppendEvent(orders[i]) } - pnl.Result.IsLiquidated = true pnlErr := bt.Statistic.AddPNLForTime(pnl) if pnlErr != nil { diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 937a8de74a6..d6fcf2cef73 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -51,7 +51,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if o.GetDirection() == common.DoNothing { return f, ErrDoNothing } - if o.GetAssetType().IsFutures() && !o.IsClosingPosition() { + if (o.GetAssetType().IsFutures() && !o.IsClosingPosition()) || o.IsLiquidating() { f.Amount = o.GetAllocatedFunds() } eventFunds := o.GetAllocatedFunds() @@ -72,6 +72,26 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * var adjustedPrice, amount decimal.Decimal if cs.UseRealOrders { + if o.IsLiquidating() { + // Liquidation occurs serverside + if o.GetAssetType().IsFutures() { + cr, err := funds.CollateralReleaser() + if err != nil { + return f, err + } + // update local records + cr.Liquidate() + + } else { + pr, err := funds.PairReleaser() + if err != nil { + return f, err + } + // update local records + pr.Liquidate() + } + return f, nil + } // get current orderbook var ob *orderbook.Base ob, err = orderbook.Get(f.Exchange, f.CurrencyPair, f.AssetType) @@ -136,7 +156,10 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.ExchangeFee = calculateExchangeFee(adjustedPrice, limitReducedAmount, cs.ExchangeFee) orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) - if !o.IsClosingPosition() { + if err != nil { + return f, err + } + if !o.IsClosingPosition() && !o.IsLiquidating() { err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) if err != nil { return f, err diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 4c8cb24db29..6b940a04307 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -619,7 +619,7 @@ func (p *Portfolio) CheckLiquidationStatus(e common.DataEventHandler, collateral return nil } -func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler) ([]*order.Order, error) { +func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler, funds funding.IFundingManager) ([]*order.Order, error) { var closingOrders []*order.Order for exch, assetMap := range p.exchangeAssetPairSettings { for item, pairMap := range assetMap { @@ -649,43 +649,50 @@ func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler) ([]*orde AssetType: pos.Asset, Reason: "LIQUIDATED", }, - ID: "", - Direction: direction, - Status: gctorder.Liquidated, - Price: ev.GetClosePrice(), - Amount: pos.Exposure, - OrderType: gctorder.Market, - ClosingPosition: true, + ID: "", + Direction: direction, + Status: gctorder.Liquidated, + Price: ev.GetClosePrice(), + Amount: pos.Exposure, + AllocatedSize: pos.Exposure, + OrderType: gctorder.Market, + LiquidatingPosition: true, }) case item == asset.Spot: - snappy := settings.ComplianceManager.GetLatestSnapshot() - if len(snappy.Orders) == 0 { - continue + allFunds := funds.GetAllFunding() + for i := range allFunds { + if allFunds[i].Asset.IsFutures() { + continue + } + if allFunds[i].Currency.IsFiatCurrency() || allFunds[i].Currency.IsStableCurrency() { + // close orders for assets, zeroing for fiat/stable + continue + } + closingOrders = append(closingOrders, &order.Order{ + Base: event.Base{ + Offset: ev.GetOffset(), + Exchange: exch, + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: pair, + AssetType: item, + Reason: "LIQUIDATED", + }, + Direction: gctorder.Sell, + Status: gctorder.Liquidated, + Amount: allFunds[i].Available, + OrderType: gctorder.Market, + AllocatedSize: allFunds[i].Available, + LiquidatingPosition: true, + }) } - what := snappy.Orders[len(snappy.Orders)-1] - closingOrders = append(closingOrders, &order.Order{ - Base: event.Base{ - Offset: ev.GetOffset(), - Exchange: exch, - Time: ev.GetTime(), - Interval: ev.GetInterval(), - CurrencyPair: pair, - AssetType: item, - Reason: "LIQUIDATED", - }, - Direction: gctorder.Sell, - Status: gctorder.Liquidated, - Price: what.ClosePrice, - Amount: decimal.NewFromFloat(what.Order.Amount), - OrderType: gctorder.Market, - AllocatedSize: decimal.NewFromFloat(what.Order.Amount), - ClosingPosition: true, - }) } } } } + funds.Liquidate() + return closingOrders, nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 0f22c99f130..8fa208b870a 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -62,7 +62,7 @@ type Handler interface { GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary CheckLiquidationStatus(common.DataEventHandler, funding.ICollateralReader, *PNLSummary) error - CreateLiquidationOrders(common.DataEventHandler) ([]*order.Order, error) + CreateLiquidationOrders(common.DataEventHandler, funding.IFundingManager) ([]*order.Order, error) Reset() } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index ba2a2bac91d..1779c892385 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -57,6 +57,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if err != nil { return nil, err } + for _, v := range sortedSignals { pos, err := p.GetPositions(v.futureSignal.Latest()) if err != nil { @@ -81,6 +82,13 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if pos != nil && pos[len(pos)-1].Status == order.Open { futuresSignal.AppendReason(fmt.Sprintf("Unrealised PNL %v", pos[len(pos)-1].UnrealisedPNL)) } + if f.HasBeenLiquidated(&spotSignal) || f.HasBeenLiquidated(&futuresSignal) { + spotSignal.AppendReason("cannot transact, has been liquidated") + futuresSignal.AppendReason("cannot transact, has been liquidated") + response = append(response, &spotSignal, &futuresSignal) + continue + } + switch { case len(pos) == 0: // check to see if order is appropriate to action diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index ce57be1d4c6..b9c2be19406 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -89,6 +89,12 @@ func (o *Order) GetFillDependentEvent() signal.Event { return o.FillDependentEvent } +// IsClosingPosition returns whether position is being closed func (o *Order) IsClosingPosition() bool { return o.ClosingPosition } + +// IsLiquidating returns whether position is being liquidated +func (o *Order) IsLiquidating() bool { + return o.LiquidatingPosition +} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index a5a8f5718cd..6480f631de3 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -11,18 +11,19 @@ import ( // Order contains all details for an order event type Order struct { event.Base - ID string - Direction order.Side - Status order.Status - Price decimal.Decimal - Amount decimal.Decimal - OrderType order.Type - Leverage decimal.Decimal - AllocatedSize decimal.Decimal - BuyLimit decimal.Decimal - SellLimit decimal.Decimal - FillDependentEvent signal.Event - ClosingPosition bool + ID string + Direction order.Side + Status order.Status + Price decimal.Decimal + Amount decimal.Decimal + OrderType order.Type + Leverage decimal.Decimal + AllocatedSize decimal.Decimal + BuyLimit decimal.Decimal + SellLimit decimal.Decimal + FillDependentEvent signal.Event + ClosingPosition bool + LiquidatingPosition bool } // Event inherits common event interfaces along with extra functions related to handling orders @@ -41,4 +42,5 @@ type Event interface { GetAllocatedFunds() decimal.Decimal GetFillDependentEvent() signal.Event IsClosingPosition() bool + IsLiquidating() bool } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index e1fd36dfb3d..cfe82c904f5 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -491,29 +491,13 @@ func (f *FundManager) getFundingForEAC(exch string, a asset.Item, c currency.Cod return nil, ErrFundsNotFound } -// LiquidateByCollateral will remove all collateral value -// and all contracts -func (f *FundManager) LiquidateByCollateral(c currency.Code) error { - found := false +// Liquidate will remove all funding +func (f *FundManager) Liquidate() { for i := range f.items { - if f.items[i].currency == c && f.items[i].isCollateral && f.items[i].asset.IsFutures() { - f.items[i].available = decimal.Zero - f.items[i].reserved = decimal.Zero - found = true - } - } - if !found { - return ErrNotCollateral - } - for i := range f.items { - if f.items[i].pairedWith != nil && - f.items[i].pairedWith.currency == c && - f.items[i].asset.IsFutures() { - f.items[i].available = decimal.Zero - f.items[i].reserved = decimal.Zero - } + f.items[i].reserved = decimal.Zero + f.items[i].available = decimal.Zero + f.items[i].isLiquidated = true } - return nil } // GetAllFunding returns basic representations of all current @@ -630,3 +614,19 @@ func (f *FundManager) RealisePNL(receivingExchange string, receivingAsset asset. } return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, realisedPNL, receivingExchange, receivingAsset, receivingCurrency) } + +// HasBeenLiquidated checks +func (f *FundManager) HasBeenLiquidated(ev common.EventHandler) bool { + for i := range f.items { + if ev.GetExchange() == f.items[i].exchange && + ev.GetAssetType() == f.items[i].asset && + (f.items[i].currency.Equal(ev.Pair().Base) || + f.items[i].currency.Equal(ev.Pair().Quote)) && + f.items[i].isLiquidated { + return true + } else { + continue + } + } + return false +} diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index cad9716a632..ab55e9ca284 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -31,10 +31,11 @@ type IFundingManager interface { AddUSDTrackingData(*kline.DataFromKline) error CreateSnapshot(time.Time) USDTrackingDisabled() bool - LiquidateByCollateral(currency.Code) error + Liquidate() GetAllFunding() []BasicItem UpdateCollateral(common.EventHandler) error HasFutures() bool + HasBeenLiquidated(ev common.EventHandler) bool RealisePNL(receivingExchange string, receivingAsset asset.Item, receivingCurrency currency.Code, realisedPNL decimal.Decimal) error } @@ -67,6 +68,7 @@ type IFundTransferer interface { IsUsingExchangeLevelFunding() bool Transfer(decimal.Decimal, *Item, *Item, bool) error GetFundingForEvent(common.EventHandler) (IFundingPair, error) + HasBeenLiquidated(ev common.EventHandler) bool } // IFundReserver limits funding usage for portfolio event handling @@ -132,6 +134,7 @@ type Item struct { trackingCandles *kline.DataFromKline snapshot map[int64]ItemSnapshot isCollateral bool + isLiquidated bool collateralCandles map[currency.Code]kline.DataFromKline } From f818f6c81245d3026605fd2326d3c5e2f0969f87 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 29 Mar 2022 16:08:37 +1100 Subject: [PATCH 110/171] Further fleshes out liquidations --- backtester/common/common_types.go | 1 + backtester/engine/backtest.go | 80 +++++++-------- backtester/eventhandlers/exchange/exchange.go | 2 +- .../portfolio/holdings/holdings_types.go | 1 + .../eventhandlers/portfolio/portfolio.go | 34 ++++--- .../portfolio/portfolio_types.go | 2 +- .../eventhandlers/portfolio/size/size.go | 8 +- .../statistics/currencystatistics.go | 22 ++--- .../eventhandlers/statistics/printresults.go | 8 +- .../eventhandlers/statistics/statistics.go | 59 ++++++++--- .../statistics/statistics_types.go | 5 +- .../dollarcostaverage/dollarcostaverage.go | 4 +- .../ftxcashandcarry/ftxcashandcarry.go | 8 +- .../eventhandlers/strategies/rsi/rsi.go | 4 +- .../strategies/strategies_types.go | 4 +- .../strategies/top2bottom2/top2bottom2.go | 4 +- .../top2bottom2/top2bottom2_test.go | 6 +- backtester/eventtypes/order/order.go | 5 + backtester/eventtypes/order/order_types.go | 3 +- backtester/eventtypes/signal/signal.go | 4 +- backtester/eventtypes/signal/signal_test.go | 2 +- backtester/eventtypes/signal/signal_types.go | 2 +- backtester/funding/funding.go | 25 +++-- backtester/funding/funding_types.go | 22 ++--- backtester/report/report.go | 97 ++++++++++--------- backtester/report/report_types.go | 11 ++- backtester/report/tpl.gohtml | 21 ++-- 27 files changed, 252 insertions(+), 192 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 47ec80aadcf..0d5dbf96817 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -75,6 +75,7 @@ type EventHandler interface { GetInterval() kline.Interval GetAssetType() asset.Item GetReason() string + GetClosePrice() decimal.Decimal AppendReason(string) } diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index db1115dc356..d09e88e40cb 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -118,22 +118,22 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { } else { err = bt.processSingleDataEvent(eType, funds.FundReleaser()) } - if err != nil { - return err - } case signal.Event: - bt.processSignalEvent(eType, funds.FundReserver()) + err = bt.processSignalEvent(eType, funds.FundReserver()) case order.Event: - bt.processOrderEvent(eType, funds.FundReleaser()) + err = bt.processOrderEvent(eType, funds.FundReleaser()) case fill.Event: - bt.processFillEvent(eType, funds.FundReleaser()) + err = bt.processFillEvent(eType, funds.FundReleaser()) default: return fmt.Errorf("handleEvent %w %T received, could not process", errUnhandledDatatype, ev) } - bt.Funding.CreateSnapshot(ev.GetTime()) + if err != nil { + return err + } + bt.Funding.CreateSnapshot(ev.GetTime()) return nil } @@ -220,7 +220,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu // update statistics with the latest price err := bt.Statistic.SetupEventForTime(ev) if err != nil { - if err == statistics.ErrAlreadyProcessed { + if errors.Is(err, statistics.ErrAlreadyProcessed) { return err } log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) @@ -239,34 +239,40 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice()) - if err != nil { - if errors.Is(err, gctorder.ErrPositionLiquidated) { - cr.Liquidate() - } else { - log.Errorf(common.SubLoggers[common.Backtester], "UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return nil - } + if err != nil && !errors.Is(err, gctorder.ErrPositionLiquidated) { + return fmt.Errorf("UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var pnl *portfolio.PNLSummary pnl, err = bt.Portfolio.GetLatestPNLForEvent(ev) if err != nil { return err } - - pnl.Result.UnrealisedPNL = decimal.NewFromInt(-444 - ev.GetOffset()) - + if !pnl.Result.IsLiquidated { + pnl.Result.UnrealisedPNL = decimal.NewFromInt(-600 - ev.GetOffset()) + } + if pnl.Result.IsLiquidated { + return nil + } err = bt.Portfolio.CheckLiquidationStatus(ev, cr, pnl) if err != nil { if errors.Is(err, gctorder.ErrPositionLiquidated) { // trigger closure of everything - orders, err := bt.Portfolio.CreateLiquidationOrders(ev, bt.Funding) + orders, err := bt.Portfolio.CreateLiquidationOrdersForExchange(ev, bt.Funding) if err != nil { return err } for i := range orders { bt.EventQueue.AppendEvent(orders[i]) + //err := bt.Statistic.SetupEventForTime(orders[i]) + //if err != nil { + // if errors.Is(err, statistics.ErrAlreadyProcessed) { + // continue + // } + // log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + //} } pnl.Result.IsLiquidated = true + pnl.Result.Status = gctorder.Liquidated pnlErr := bt.Statistic.AddPNLForTime(pnl) if pnlErr != nil { return pnlErr @@ -282,33 +288,34 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } // processSignalEvent receives an event from the strategy for processing under the portfolio -func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) { +func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) error { cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var o *order.Order o, err = bt.Portfolio.OnSignal(ev, &cs, funds) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } err = bt.Statistic.SetEventForOffset(o) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return fmt.Errorf("SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(o) + return nil } -func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundReleaser) { +func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundReleaser) error { d := bt.Datas.GetDataForCurrency(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) f, err := bt.Exchange.ExecuteOrder(ev, d, bt.orderManager, funds) if err != nil { if f == nil { log.Errorf(common.SubLoggers[common.Backtester], "ExecuteOrder fill event should always be returned, please fix, %v", err) - return + return fmt.Errorf("ExecuteOrder fill event should always be returned, please fix, %v", err) } if !errors.Is(err, exchange.ErrDoNothing) { log.Errorf(common.SubLoggers[common.Backtester], "ExecuteOrder %v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) @@ -319,13 +326,13 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(f) + return nil } -func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) { +func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) error { t, err := bt.Portfolio.OnFill(ev, funds) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("OnFill %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } err = bt.Statistic.SetEventForOffset(t) if err != nil { @@ -372,15 +379,13 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if ev.GetOrder() != nil { pnl, err := bt.Portfolio.TrackFuturesOrder(ev, funds) if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { - log.Errorf(common.SubLoggers[common.Backtester], "TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var exch gctexchange.IBotExchange exch, err = bt.exchangeManager.GetExchangeByName(ev.GetExchange()) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } rPNL := pnl.GetRealisedPNL() @@ -389,13 +394,11 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) var receivingAsset asset.Item receivingCurrency, receivingAsset, err = exch.GetCurrencyForRealisedPNL(ev.GetAssetType(), ev.Pair()) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } err = bt.Funding.RealisePNL(ev.GetExchange(), receivingAsset, receivingCurrency, rPNL.PNL) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "RealisePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("RealisePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } @@ -406,11 +409,10 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) } err = bt.Funding.UpdateCollateral(ev) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - return + return fmt.Errorf("UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } - } + return nil } // Stop shuts down the live data loop diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index d6fcf2cef73..12122466a53 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -51,7 +51,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if o.GetDirection() == common.DoNothing { return f, ErrDoNothing } - if (o.GetAssetType().IsFutures() && !o.IsClosingPosition()) || o.IsLiquidating() { + if o.GetAssetType().IsFutures() && !o.IsClosingPosition() { f.Amount = o.GetAllocatedFunds() } eventFunds := o.GetAllocatedFunds() diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_types.go b/backtester/eventhandlers/portfolio/holdings/holdings_types.go index f7aa55aa473..c65cebb567f 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_types.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_types.go @@ -32,6 +32,7 @@ type Holding struct { SoldValue decimal.Decimal `json:"sold-value"` BoughtAmount decimal.Decimal `json:"bought-amount"` BoughtValue decimal.Decimal `json:"bought-value"` + IsLiquidated bool TotalValueDifference decimal.Decimal ChangeInTotalValuePercent decimal.Decimal diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 6b940a04307..5ab381d04c3 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -55,6 +55,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi Direction: ev.GetDirection(), FillDependentEvent: ev.GetFillDependentEvent(), Amount: ev.GetAmount(), + ClosePrice: ev.GetClosePrice(), } if ev.GetDirection() == "" { return o, errInvalidDirection @@ -79,7 +80,6 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return cannotPurchase(ev, o) } - o.Price = ev.GetPrice() o.OrderType = gctorder.Market o.BuyLimit = ev.GetBuyLimit() o.SellLimit = ev.GetSellLimit() @@ -227,13 +227,13 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi sizedOrder.AllocatedSize = sizedOrder.Amount case gctorder.Short, gctorder.Long: err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) - sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.Price) + sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) case common.ClosePosition: err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) - sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.Price) + sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) default: - err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.Price), gctorder.Buy) - sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.Price) + err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice), gctorder.Buy) + sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice) } if err != nil { sizedOrder.Direction = common.DoNothing @@ -612,16 +612,21 @@ func (p *Portfolio) CheckLiquidationStatus(e common.DataEventHandler, collateral } if !position.Status.IsInactive() && pnl.Result.UnrealisedPNL.IsNegative() && - pnl.Result.UnrealisedPNL.Abs().GreaterThan(availableFunds) { + pnl.Result.UnrealisedPNL.Abs().GreaterThan(availableFunds) && + availableFunds.GreaterThan(decimal.Zero) { return gctorder.ErrPositionLiquidated } return nil } -func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler, funds funding.IFundingManager) ([]*order.Order, error) { - var closingOrders []*order.Order +func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandler, funds funding.IFundingManager) ([]order.Event, error) { + var closingOrders []order.Event for exch, assetMap := range p.exchangeAssetPairSettings { + if exch != ev.GetExchange() { + // only liquidate the same exchange + continue + } for item, pairMap := range assetMap { for pair, settings := range pairMap { switch { @@ -649,16 +654,18 @@ func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler, funds fu AssetType: pos.Asset, Reason: "LIQUIDATED", }, - ID: "", Direction: direction, Status: gctorder.Liquidated, - Price: ev.GetClosePrice(), + ClosePrice: ev.GetClosePrice(), Amount: pos.Exposure, AllocatedSize: pos.Exposure, OrderType: gctorder.Market, LiquidatingPosition: true, }) - + err := settings.FuturesTracker.Liquidate(ev.GetClosePrice(), ev.GetTime()) + if err != nil { + return nil, err + } case item == asset.Spot: allFunds := funds.GetAllFunding() for i := range allFunds { @@ -666,7 +673,8 @@ func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler, funds fu continue } if allFunds[i].Currency.IsFiatCurrency() || allFunds[i].Currency.IsStableCurrency() { - // close orders for assets, zeroing for fiat/stable + // close orders for assets + // funding manager will zero for fiat/stable continue } closingOrders = append(closingOrders, &order.Order{ @@ -691,7 +699,7 @@ func (p *Portfolio) CreateLiquidationOrders(ev common.DataEventHandler, funds fu } } } - funds.Liquidate() + funds.Liquidate(ev) return closingOrders, nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 8fa208b870a..409c3a0bbec 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -62,7 +62,7 @@ type Handler interface { GetLatestPNLForEvent(common.EventHandler) (*PNLSummary, error) GetLatestPNLs() []PNLSummary CheckLiquidationStatus(common.DataEventHandler, funding.ICollateralReader, *PNLSummary) error - CreateLiquidationOrders(common.DataEventHandler, funding.IFundingManager) ([]*order.Order, error) + CreateLiquidationOrdersForExchange(common.DataEventHandler, funding.IFundingManager) ([]order.Event, error) Reset() } diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 19446673e29..d19d1c2f9cc 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -29,13 +29,13 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc amount = amountAvailable case gctorder.Buy, gctorder.Long: // check size against currency specific settings - amount, err = s.calculateBuySize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) + amount, err = s.calculateBuySize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) if err != nil { return nil, err } // check size against portfolio specific settings var portfolioSize decimal.Decimal - portfolioSize, err = s.calculateBuySize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) + portfolioSize, err = s.calculateBuySize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) if err != nil { return nil, err } @@ -45,12 +45,12 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc } case gctorder.Sell, gctorder.Short: // check size against currency specific settings - amount, err = s.calculateSellSize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) + amount, err = s.calculateSellSize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) if err != nil { return nil, err } // check size against portfolio specific settings - portfolioSize, err := s.calculateSellSize(retOrder.Price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) + portfolioSize, err := s.calculateSellSize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) if err != nil { return nil, err } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index c20665e8477..22cff608a26 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -18,9 +18,9 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e first := c.Events[0] sep := fmt.Sprintf("%v %v %v |\t", first.DataEvent.GetExchange(), first.DataEvent.GetAssetType(), first.DataEvent.Pair()) - firstPrice := first.DataEvent.GetClosePrice() + firstPrice := first.ClosePrice last := c.Events[len(c.Events)-1] - lastPrice := last.DataEvent.GetClosePrice() + lastPrice := last.ClosePrice for i := range last.Transactions.Orders { switch last.Transactions.Orders[i].Order.Side { case gctorder.Buy: @@ -34,15 +34,15 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e } } for i := range c.Events { - price := c.Events[i].DataEvent.GetClosePrice() + price := c.Events[i].ClosePrice if price.LessThan(c.LowestClosePrice.Value) || !c.LowestClosePrice.Set { c.LowestClosePrice.Value = price - c.LowestClosePrice.Time = c.Events[i].DataEvent.GetTime() + c.LowestClosePrice.Time = c.Events[i].Time c.LowestClosePrice.Set = true } if price.GreaterThan(c.HighestClosePrice.Value) { c.HighestClosePrice.Value = price - c.HighestClosePrice.Time = c.Events[i].DataEvent.GetTime() + c.HighestClosePrice.Time = c.Events[i].Time c.HighestClosePrice.Set = true } } @@ -69,16 +69,16 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e if c.Events[i].SignalEvent != nil && c.Events[i].SignalEvent.GetDirection() == common.MissingData { c.ShowMissingDataWarning = true } - if c.Events[i].DataEvent.GetClosePrice().IsZero() || c.Events[i-1].DataEvent.GetClosePrice().IsZero() { + if c.Events[i].ClosePrice.IsZero() || c.Events[i-1].ClosePrice.IsZero() { // closing price for the current candle or previous candle is zero, use the previous // benchmark rate to allow some consistency c.ShowMissingDataWarning = true benchmarkRates[i] = benchmarkRates[i-1] continue } - benchmarkRates[i] = c.Events[i].DataEvent.GetClosePrice().Sub( - c.Events[i-1].DataEvent.GetClosePrice()).Div( - c.Events[i-1].DataEvent.GetClosePrice()) + benchmarkRates[i] = c.Events[i].ClosePrice.Sub( + c.Events[i-1].ClosePrice).Div( + c.Events[i-1].ClosePrice) } // remove the first entry as its zero and impacts @@ -132,8 +132,8 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() { for i := range c.Events { - if c.Events[i].Holdings.BaseSize.Mul(c.Events[i].DataEvent.GetClosePrice()).GreaterThan(c.HighestCommittedFunds.Value) { - c.HighestCommittedFunds.Value = c.Events[i].Holdings.BaseSize.Mul(c.Events[i].DataEvent.GetClosePrice()) + if c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice).GreaterThan(c.HighestCommittedFunds.Value) { + c.HighestCommittedFunds.Value = c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice) c.HighestCommittedFunds.Time = c.Events[i].Holdings.Timestamp } } diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 9074173b219..b585ce708ed 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -124,7 +124,7 @@ func (s *Statistic) PrintAllEventsChronologically() { fSIL(exch, limit12), fSIL(a.String(), limit10), fSIL(currencyStatistic.Events[i].SignalEvent.Pair().String(), limit14), - currencyStatistic.Events[i].SignalEvent.GetPrice().Round(8)) + currencyStatistic.Events[i].SignalEvent.GetClosePrice().Round(8)) msg = addReason(currencyStatistic.Events[i].SignalEvent.GetReason(), msg) msg = msg + common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].SignalEvent.GetTime(), msg) @@ -168,14 +168,14 @@ func (s *Statistic) PrintAllEventsChronologically() { func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency.Pair, usingExchangeLevelFunding bool) { var errs gctcommon.Errors sort.Slice(c.Events, func(i, j int) bool { - return c.Events[i].DataEvent.GetTime().Before(c.Events[j].DataEvent.GetTime()) + return c.Events[i].Time.Before(c.Events[j].Time) }) last := c.Events[len(c.Events)-1] first := c.Events[0] c.StartingClosePrice.Value = first.DataEvent.GetClosePrice() - c.StartingClosePrice.Time = first.DataEvent.GetTime() + c.StartingClosePrice.Time = first.Time c.EndingClosePrice.Value = last.DataEvent.GetClosePrice() - c.EndingClosePrice.Time = last.DataEvent.GetTime() + c.EndingClosePrice.Time = last.Time c.TotalOrders = c.BuyOrders + c.SellOrders + c.ShortOrders + c.LongOrders last.Holdings.TotalValueLost = last.Holdings.TotalValueLostToSlippage.Add(last.Holdings.TotalValueLostToVolumeSizing) sep := fmt.Sprintf("%v %v %v |\t", fSIL(e, limit12), fSIL(a.String(), limit10), fSIL(p.String(), limit14)) diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index a307a2761e9..4264074ae34 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -23,7 +23,7 @@ func (s *Statistic) Reset() { } // SetupEventForTime sets up the big map for to store important data at each time interval -func (s *Statistic) SetupEventForTime(ev common.DataEventHandler) error { +func (s *Statistic) SetupEventForTime(ev common.EventHandler) error { if ev == nil { return common.ErrNilEvent } @@ -40,16 +40,41 @@ func (s *Statistic) SetupEventForTime(ev common.DataEventHandler) error { } } for i := range lookup.Events { - if lookup.Events[i].DataEvent.GetTime().Equal(ev.GetTime()) && - lookup.Events[i].DataEvent.GetOffset() == ev.GetOffset() { - return ErrAlreadyProcessed + if lookup.Events[i].Offset != ev.GetOffset() { + continue + } + switch ev.(type) { + case common.DataEventHandler: + if lookup.Events[i].DataEvent != nil { + return ErrAlreadyProcessed + } + case order.Event: + if lookup.Events[i].OrderEvent != nil { + return ErrAlreadyProcessed + } + default: + return fmt.Errorf("%w %v %v %v %v", errNoRelevantStatsFound, ev.GetOffset(), ev.GetExchange(), ev.GetAssetType(), ev.Pair()) } } - lookup.Events = append(lookup.Events, - DataAtOffset{ - DataEvent: ev, - }, - ) + switch t := ev.(type) { + case common.DataEventHandler: + lookup.Events = append(lookup.Events, + DataAtOffset{ + DataEvent: t, + Offset: t.GetOffset(), + Time: t.GetTime(), + }, + ) + case order.Event: + lookup.Events = append(lookup.Events, + DataAtOffset{ + OrderEvent: t, + Offset: t.GetOffset(), + Time: t.GetTime(), + }, + ) + } + s.ExchangeAssetPairStatistics[ex][a][p] = lookup return nil @@ -84,7 +109,7 @@ func (s *Statistic) SetEventForOffset(ev common.EventHandler) error { return fmt.Errorf("%w for %v %v %v to set signal event", errCurrencyStatisticsUnset, exch, a, p) } for i := len(lookup.Events) - 1; i >= 0; i-- { - if lookup.Events[i].DataEvent.GetOffset() == offset { + if lookup.Events[i].Offset == offset { return applyEventAtOffset(ev, lookup, i) } } @@ -105,6 +130,10 @@ func applyEventAtOffset(ev common.EventHandler, lookup *CurrencyPairStatistic, i default: return fmt.Errorf("unknown event type received: %v", ev) } + lookup.Events[i].Time = ev.GetTime() + lookup.Events[i].ClosePrice = ev.GetClosePrice() + lookup.Events[i].Offset = ev.GetOffset() + return nil } @@ -118,7 +147,7 @@ func (s *Statistic) AddHoldingsForTime(h *holdings.Holding) error { return fmt.Errorf("%w for %v %v %v to set holding event", errCurrencyStatisticsUnset, h.Exchange, h.Asset, h.Pair) } for i := len(lookup.Events) - 1; i >= 0; i-- { - if lookup.Events[i].DataEvent.GetOffset() == h.Offset { + if lookup.Events[i].Offset == h.Offset { lookup.Events[i].Holdings = *h return nil } @@ -138,7 +167,7 @@ func (s *Statistic) AddPNLForTime(pnl *portfolio.PNLSummary) error { return fmt.Errorf("%w for %v %v %v to set pnl", errCurrencyStatisticsUnset, pnl.Exchange, pnl.Item, pnl.Pair) } for i := len(lookup.Events) - 1; i >= 0; i-- { - if lookup.Events[i].DataEvent.GetOffset() == pnl.Offset { + if lookup.Events[i].Offset == pnl.Offset { lookup.Events[i].PNL = pnl lookup.Events[i].Holdings.BaseSize = pnl.Result.Exposure return nil @@ -163,7 +192,7 @@ func (s *Statistic) AddComplianceSnapshotForTime(c compliance.Snapshot, e fill.E return fmt.Errorf("%w for %v %v %v to set compliance snapshot", errCurrencyStatisticsUnset, exch, a, p) } for i := len(lookup.Events) - 1; i >= 0; i-- { - if lookup.Events[i].DataEvent.GetOffset() == e.GetOffset() { + if lookup.Events[i].Offset == e.GetOffset() { lookup.Events[i].Transactions = c return nil } @@ -195,8 +224,8 @@ func (s *Statistic) CalculateAllResults() error { stats.FinalHoldings = last.Holdings stats.InitialHoldings = stats.Events[0].Holdings stats.FinalOrders = last.Transactions - s.StartDate = stats.Events[0].DataEvent.GetTime() - s.EndDate = last.DataEvent.GetTime() + s.StartDate = stats.Events[0].Time + s.EndDate = last.Time finalResults = append(finalResults, FinalResultsHolder{ Exchange: exchangeName, diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 0fb6c4fb238..9dd1ca814d9 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -69,7 +69,7 @@ type FinalResultsHolder struct { // Handler interface details what a statistic is expected to do type Handler interface { SetStrategyName(string) - SetupEventForTime(common.DataEventHandler) error + SetupEventForTime(handler common.EventHandler) error SetEventForOffset(common.EventHandler) error AddHoldingsForTime(*holdings.Holding) error AddComplianceSnapshotForTime(compliance.Snapshot, fill.Event) error @@ -121,6 +121,9 @@ type CurrencyStats interface { // DataAtOffset is used to hold all event information // at a time interval type DataAtOffset struct { + Offset int64 + ClosePrice decimal.Decimal + Time time.Time Holdings holdings.Holding Transactions compliance.Snapshot DataEvent common.DataEventHandler diff --git a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go index 9fc3b79f966..d948f9778a0 100644 --- a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go +++ b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage.go @@ -37,7 +37,7 @@ func (s *Strategy) Description() string { // OnSignal handles a data event and returns what action the strategy believes should occur // For dollarcostaverage, this means returning a buy signal on every event -func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) (signal.Event, error) { +func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundingTransferer, _ portfolio.Handler) (signal.Event, error) { if d == nil { return nil, common.ErrNilEvent } @@ -66,7 +66,7 @@ func (s *Strategy) SupportsSimultaneousProcessing() bool { // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z // For dollarcostaverage, the strategy is always "buy", so it uses the OnSignal function -func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundingTransferer, _ portfolio.Handler) ([]signal.Event, error) { var resp []signal.Event var errs gctcommon.Errors for i := range d { diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 1779c892385..e48d23f21f7 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -31,7 +31,7 @@ func (s *Strategy) Description() string { // OnSignal handles a data event and returns what action the strategy believes should occur // For rsi, this means returning a buy signal when rsi is at or below a certain level, and a // sell signal when it is at or above a certain level -func (s *Strategy) OnSignal(data.Handler, funding.IFundTransferer, portfolio.Handler) (signal.Event, error) { +func (s *Strategy) OnSignal(data.Handler, funding.IFundingTransferer, portfolio.Handler) (signal.Event, error) { return nil, base.ErrSimultaneousProcessingOnly } @@ -51,7 +51,7 @@ var errNotSetup = errors.New("sent incomplete signals") // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z -func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransferer, p portfolio.Handler) ([]signal.Event, error) { +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTransferer, p portfolio.Handler) ([]signal.Event, error) { var response []signal.Event sortedSignals, err := sortSignals(d, f) if err != nil { @@ -82,7 +82,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf if pos != nil && pos[len(pos)-1].Status == order.Open { futuresSignal.AppendReason(fmt.Sprintf("Unrealised PNL %v", pos[len(pos)-1].UnrealisedPNL)) } - if f.HasBeenLiquidated(&spotSignal) || f.HasBeenLiquidated(&futuresSignal) { + if f.HasExchangeBeenLiquidated(&spotSignal) || f.HasExchangeBeenLiquidated(&futuresSignal) { spotSignal.AppendReason("cannot transact, has been liquidated") futuresSignal.AppendReason("cannot transact, has been liquidated") response = append(response, &spotSignal, &futuresSignal) @@ -141,7 +141,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransf return response, nil } -func sortSignals(d []data.Handler, f funding.IFundTransferer) (map[currency.Pair]cashCarrySignals, error) { +func sortSignals(d []data.Handler, f funding.IFundingTransferer) (map[currency.Pair]cashCarrySignals, error) { var response = make(map[currency.Pair]cashCarrySignals) for i := range d { l := d[i].Latest() diff --git a/backtester/eventhandlers/strategies/rsi/rsi.go b/backtester/eventhandlers/strategies/rsi/rsi.go index 0fbb3448472..795383a131d 100644 --- a/backtester/eventhandlers/strategies/rsi/rsi.go +++ b/backtester/eventhandlers/strategies/rsi/rsi.go @@ -47,7 +47,7 @@ func (s *Strategy) Description() string { // OnSignal handles a data event and returns what action the strategy believes should occur // For rsi, this means returning a buy signal when rsi is at or below a certain level, and a // sell signal when it is at or above a certain level -func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) (signal.Event, error) { +func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundingTransferer, _ portfolio.Handler) (signal.Event, error) { if d == nil { return nil, common.ErrNilEvent } @@ -99,7 +99,7 @@ func (s *Strategy) SupportsSimultaneousProcessing() bool { // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z -func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, _ funding.IFundingTransferer, _ portfolio.Handler) ([]signal.Event, error) { var resp []signal.Event var errs gctcommon.Errors for i := range d { diff --git a/backtester/eventhandlers/strategies/strategies_types.go b/backtester/eventhandlers/strategies/strategies_types.go index 590694c8d1e..f7fdd4a59a7 100644 --- a/backtester/eventhandlers/strategies/strategies_types.go +++ b/backtester/eventhandlers/strategies/strategies_types.go @@ -11,8 +11,8 @@ import ( type Handler interface { Name() string Description() string - OnSignal(data.Handler, funding.IFundTransferer, portfolio.Handler) (signal.Event, error) - OnSimultaneousSignals([]data.Handler, funding.IFundTransferer, portfolio.Handler) ([]signal.Event, error) + OnSignal(data.Handler, funding.IFundingTransferer, portfolio.Handler) (signal.Event, error) + OnSimultaneousSignals([]data.Handler, funding.IFundingTransferer, portfolio.Handler) ([]signal.Event, error) UsingSimultaneousProcessing() bool SupportsSimultaneousProcessing() bool SetSimultaneousProcessing(bool) diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go index 3605d1e9028..f505f183aab 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2.go @@ -53,7 +53,7 @@ func (s *Strategy) Description() string { // OnSignal handles a data event and returns what action the strategy believes should occur // however,this complex strategy cannot function on an individual basis -func (s *Strategy) OnSignal(_ data.Handler, _ funding.IFundTransferer, _ portfolio.Handler) (signal.Event, error) { +func (s *Strategy) OnSignal(_ data.Handler, _ funding.IFundingTransferer, _ portfolio.Handler) (signal.Event, error) { return nil, errStrategyOnlySupportsSimultaneousProcessing } @@ -88,7 +88,7 @@ func sortByMFI(o *[]mfiFundEvent, reverse bool) { // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z -func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundTransferer, _ portfolio.Handler) ([]signal.Event, error) { +func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTransferer, _ portfolio.Handler) ([]signal.Event, error) { if len(d) < 4 { return nil, errStrategyCurrencyRequirements } diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go index 8e6b57b4c35..fa3fe3eef3a 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go @@ -214,15 +214,15 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { for i := range resp { switch resp[i].GetDirection() { case order.Buy: - if !resp[i].GetPrice().Equal(decimal.NewFromInt(1)) && !resp[i].GetPrice().Equal(decimal.NewFromInt(2)) { + if !resp[i].GetClosePrice().Equal(decimal.NewFromInt(1)) && !resp[i].GetClosePrice().Equal(decimal.NewFromInt(2)) { t.Error("expected 1 or 2") } case order.Sell: - if !resp[i].GetPrice().Equal(decimal.NewFromInt(99)) && !resp[i].GetPrice().Equal(decimal.NewFromInt(98)) { + if !resp[i].GetClosePrice().Equal(decimal.NewFromInt(99)) && !resp[i].GetClosePrice().Equal(decimal.NewFromInt(98)) { t.Error("expected 99 or 98") } case common.DoNothing: - if !resp[i].GetPrice().Equal(decimal.NewFromInt(50)) { + if !resp[i].GetClosePrice().Equal(decimal.NewFromInt(50)) { t.Error("expected 50") } } diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index b9c2be19406..f0eac6791f9 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -98,3 +98,8 @@ func (o *Order) IsClosingPosition() bool { func (o *Order) IsLiquidating() bool { return o.LiquidatingPosition } + +// GetClosePrice returns the close price +func (o *Order) GetClosePrice() decimal.Decimal { + return o.ClosePrice +} diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index 6480f631de3..e3c8df668da 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -14,7 +14,7 @@ type Order struct { ID string Direction order.Side Status order.Status - Price decimal.Decimal + ClosePrice decimal.Decimal Amount decimal.Decimal OrderType order.Type Leverage decimal.Decimal @@ -30,6 +30,7 @@ type Order struct { type Event interface { common.EventHandler common.Directioner + GetClosePrice() decimal.Decimal GetBuyLimit() decimal.Decimal GetSellLimit() decimal.Decimal SetAmount(decimal.Decimal) diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index 4fe8b49f016..39fbc5d8b56 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -46,8 +46,8 @@ func (s *Signal) Pair() currency.Pair { return s.CurrencyPair } -// GetPrice returns the price -func (s *Signal) GetPrice() decimal.Decimal { +// GetClosePrice returns the price +func (s *Signal) GetClosePrice() decimal.Decimal { return s.ClosePrice } diff --git a/backtester/eventtypes/signal/signal_test.go b/backtester/eventtypes/signal/signal_test.go index d6e223eb9ff..98c77769a33 100644 --- a/backtester/eventtypes/signal/signal_test.go +++ b/backtester/eventtypes/signal/signal_test.go @@ -30,7 +30,7 @@ func TestSetPrice(t *testing.T) { ClosePrice: decimal.NewFromInt(1), } s.SetPrice(decimal.NewFromInt(1337)) - if !s.GetPrice().Equal(decimal.NewFromInt(1337)) { + if !s.GetClosePrice().Equal(decimal.NewFromInt(1337)) { t.Error("expected decimal.NewFromInt(1337)") } } diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index aa0fb9d6963..c23f6a8545f 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -14,7 +14,7 @@ type Event interface { common.EventHandler common.Directioner - GetPrice() decimal.Decimal + GetClosePrice() decimal.Decimal IsSignal() bool GetSellLimit() decimal.Decimal GetBuyLimit() decimal.Decimal diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index cfe82c904f5..2abaaadd03c 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -491,12 +491,14 @@ func (f *FundManager) getFundingForEAC(exch string, a asset.Item, c currency.Cod return nil, ErrFundsNotFound } -// Liquidate will remove all funding -func (f *FundManager) Liquidate() { +// Liquidate will remove all funding for all items belonging to an exchange +func (f *FundManager) Liquidate(ev common.EventHandler) { for i := range f.items { - f.items[i].reserved = decimal.Zero - f.items[i].available = decimal.Zero - f.items[i].isLiquidated = true + if f.items[i].exchange == ev.GetExchange() { + f.items[i].reserved = decimal.Zero + f.items[i].available = decimal.Zero + f.items[i].isLiquidated = true + } } } @@ -615,15 +617,12 @@ func (f *FundManager) RealisePNL(receivingExchange string, receivingAsset asset. return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, realisedPNL, receivingExchange, receivingAsset, receivingCurrency) } -// HasBeenLiquidated checks -func (f *FundManager) HasBeenLiquidated(ev common.EventHandler) bool { +// HasBeenLiquidated checks for any items with a matching exchange +// and returns whether it has been liquidated +func (f *FundManager) HasExchangeBeenLiquidated(ev common.EventHandler) bool { for i := range f.items { - if ev.GetExchange() == f.items[i].exchange && - ev.GetAssetType() == f.items[i].asset && - (f.items[i].currency.Equal(ev.Pair().Base) || - f.items[i].currency.Equal(ev.Pair().Quote)) && - f.items[i].isLiquidated { - return true + if ev.GetExchange() == f.items[i].exchange { + return f.items[i].isLiquidated } else { continue } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index ab55e9ca284..a27309fb14a 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -31,14 +31,23 @@ type IFundingManager interface { AddUSDTrackingData(*kline.DataFromKline) error CreateSnapshot(time.Time) USDTrackingDisabled() bool - Liquidate() + Liquidate(common.EventHandler) GetAllFunding() []BasicItem UpdateCollateral(common.EventHandler) error HasFutures() bool - HasBeenLiquidated(ev common.EventHandler) bool + HasExchangeBeenLiquidated(handler common.EventHandler) bool RealisePNL(receivingExchange string, receivingAsset asset.Item, receivingCurrency currency.Code, realisedPNL decimal.Decimal) error } +// IFundingTransferer allows for funding amounts to be transferred +// implementation can be swapped for live transferring +type IFundingTransferer interface { + IsUsingExchangeLevelFunding() bool + Transfer(decimal.Decimal, *Item, *Item, bool) error + GetFundingForEvent(common.EventHandler) (IFundingPair, error) + HasExchangeBeenLiquidated(handler common.EventHandler) bool +} + // IFundingReader is a simple interface of // IFundingManager for readonly access at portfolio // manager @@ -62,15 +71,6 @@ type IFundReader interface { GetCollateralReader() (ICollateralReader, error) } -// IFundTransferer allows for funding amounts to be transferred -// implementation can be swapped for live transferring -type IFundTransferer interface { - IsUsingExchangeLevelFunding() bool - Transfer(decimal.Decimal, *Item, *Item, bool) error - GetFundingForEvent(common.EventHandler) (IFundingPair, error) - HasBeenLiquidated(ev common.EventHandler) bool -} - // IFundReserver limits funding usage for portfolio event handling type IFundReserver interface { IFundReader diff --git a/backtester/report/report.go b/backtester/report/report.go index b8178c33917..adcb4bb69c8 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -86,11 +86,11 @@ func (d *Data) GenerateReport() error { // CreateUSDTotalsChart used for creating a chart in the HTML report // to show how much the overall assets are worth over time -func (d *Data) CreateUSDTotalsChart() []TotalsChart { +func (d *Data) CreateUSDTotalsChart() *Chart { if d.Statistics.FundingStatistics == nil || d.Statistics.FundingStatistics.Report.DisableUSDTracking { return nil } - var response []TotalsChart + response := &Chart{} var usdTotalChartPlot []ChartPlot for i := range d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues { usdTotalChartPlot = append(usdTotalChartPlot, ChartPlot{ @@ -98,7 +98,7 @@ func (d *Data) CreateUSDTotalsChart() []TotalsChart { UnixMilli: d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues[i].Time.UTC().UnixMilli(), }) } - response = append(response, TotalsChart{ + response.Data = append(response.Data, TotalsChart{ Name: "Total USD value", DataPoints: usdTotalChartPlot, }) @@ -111,7 +111,7 @@ func (d *Data) CreateUSDTotalsChart() []TotalsChart { UnixMilli: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), }) } - response = append(response, TotalsChart{ + response.Data = append(response.Data, TotalsChart{ Name: fmt.Sprintf("%v %v %v USD value", d.Statistics.FundingStatistics.Items[i].ReportItem.Exchange, d.Statistics.FundingStatistics.Items[i].ReportItem.Asset, d.Statistics.FundingStatistics.Items[i].ReportItem.Currency), DataPoints: plots, }) @@ -120,22 +120,64 @@ func (d *Data) CreateUSDTotalsChart() []TotalsChart { return response } +func (d *Data) createPNLCharts() *Chart { + response := &Chart{} + for exch, assetMap := range d.Statistics.ExchangeAssetPairStatistics { + for item, pairMap := range assetMap { + for pair, result := range pairMap { + id := fmt.Sprintf("%v %v %v", + exch, + item, + pair) + uPNLName := fmt.Sprintf("%v Unrealised PNL", id) + rPNLName := fmt.Sprintf("%v Realised PNL", id) + + unrealisedPNL := TotalsChart{Name: uPNLName} + realisedPNL := TotalsChart{Name: rPNLName} + for i := range result.Events { + if result.Events[i].PNL != nil { + realisedPNL.DataPoints = append(realisedPNL.DataPoints, ChartPlot{ + Value: result.Events[i].PNL.GetRealisedPNL().PNL.InexactFloat64(), + UnixMilli: result.Events[i].Time.UnixMilli(), + }) + unrealisedPNL.DataPoints = append(unrealisedPNL.DataPoints, ChartPlot{ + Value: result.Events[i].PNL.GetUnrealisedPNL().PNL.InexactFloat64(), + UnixMilli: result.Events[i].Time.UnixMilli(), + }) + } + } + if len(unrealisedPNL.DataPoints) == 0 || len(realisedPNL.DataPoints) == 0 { + continue + } + response.Data = append(response.Data, unrealisedPNL, realisedPNL) + } + } + + } + return response +} + // CreateHoldingsOverTimeChart used for creating a chart in the HTML report // to show how many holdings of each type was held over the time of backtesting -func (d *Data) CreateHoldingsOverTimeChart() []TotalsChart { +func (d *Data) CreateHoldingsOverTimeChart() *Chart { if d.Statistics.FundingStatistics == nil { return nil } - var response []TotalsChart + response := &Chart{} + response.AxisType = "logarithmic" for i := range d.Statistics.FundingStatistics.Items { var plots []ChartPlot for j := range d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots { + if d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Available.IsZero() { + // highcharts can't render zeroes in logarithmic mode + response.AxisType = "linear" + } plots = append(plots, ChartPlot{ Value: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Available.InexactFloat64(), UnixMilli: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), }) } - response = append(response, TotalsChart{ + response.Data = append(response.Data, TotalsChart{ Name: fmt.Sprintf("%v %v %v holdings", d.Statistics.FundingStatistics.Items[i].ReportItem.Exchange, d.Statistics.FundingStatistics.Items[i].ReportItem.Asset, d.Statistics.FundingStatistics.Items[i].ReportItem.Currency), DataPoints: plots, }) @@ -160,43 +202,6 @@ func (d *Data) UpdateItem(k *kline.Item) { } } -func (d *Data) createPNLCharts() []TotalsChart { - var resp []TotalsChart - for exch, assetMap := range d.Statistics.ExchangeAssetPairStatistics { - for item, pairMap := range assetMap { - for pair, result := range pairMap { - id := fmt.Sprintf("%v %v %v", - exch, - item, - pair) - uPNLName := fmt.Sprintf("%v Unrealised PNL", id) - rPNLName := fmt.Sprintf("%v Realised PNL", id) - - unrealisedPNL := TotalsChart{Name: uPNLName} - realisedPNL := TotalsChart{Name: rPNLName} - for i := range result.Events { - if result.Events[i].PNL != nil { - realisedPNL.DataPoints = append(realisedPNL.DataPoints, ChartPlot{ - Value: result.Events[i].PNL.GetRealisedPNL().PNL.InexactFloat64(), - UnixMilli: result.Events[i].PNL.GetRealisedPNL().Time.UnixMilli(), - }) - unrealisedPNL.DataPoints = append(unrealisedPNL.DataPoints, ChartPlot{ - Value: result.Events[i].PNL.GetUnrealisedPNL().PNL.InexactFloat64(), - UnixMilli: result.Events[i].PNL.GetUnrealisedPNL().Time.UnixMilli(), - }) - } - } - if len(unrealisedPNL.DataPoints) == 0 || len(realisedPNL.DataPoints) == 0 { - continue - } - resp = append(resp, unrealisedPNL, realisedPNL) - } - } - - } - return resp -} - // enhanceCandles will enhance candle data with order information allowing // report charts to have annotations to highlight buy and sell events func (d *Data) enhanceCandles() error { @@ -246,8 +251,8 @@ func (d *Data) enhanceCandles() error { } } if !requiresIteration { - if statsForCandles.Events[intVal].SignalEvent.GetTime().Equal(d.OriginalCandles[intVal].Candles[j].Time) && - statsForCandles.Events[intVal].SignalEvent.GetDirection() == common.MissingData && + if statsForCandles.Events[intVal].Time.Equal(d.OriginalCandles[intVal].Candles[j].Time) && + (statsForCandles.Events[intVal].SignalEvent == nil || statsForCandles.Events[intVal].SignalEvent.GetDirection() == common.MissingData) && len(enhancedKline.Candles) > 0 { enhancedCandle.copyCloseFromPreviousEvent(&enhancedKline) } diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index e115c68786c..e7e74af4963 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -39,12 +39,17 @@ type Data struct { OutputPath string Warnings []Warning UseDarkTheme bool - USDTotalsChart []TotalsChart - HoldingsOverTimeChart []TotalsChart - PNLOverTimeChart []TotalsChart + USDTotalsChart *Chart + HoldingsOverTimeChart *Chart + PNLOverTimeChart *Chart Prettify PrettyNumbers } +type Chart struct { + AxisType string + Data []TotalsChart +} + // TotalsChart holds chart plot data // to render charts in the report type TotalsChart struct { diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index a5d742af786..0b8a1c99f1f 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -780,7 +780,7 @@ Highcharts.setOptions(Highcharts.theme);

PNL Over Time

-
+
+

Futures Spot Diff %

+
+ +
{{ if eq $.Config.StrategySettings.DisableUSDTracking false }}
@@ -888,7 +953,7 @@ pointStart: {{ $.Statistics.StartDate.UnixMilli }}, pointInterval: {{$.Statistics.CandleInterval.Duration.Milliseconds}}, data: [ - {{ range .DataPoints }} + {{ range .LinePlots }} [{{.UnixMilli}}, {{.Value}}], {{end}} ] @@ -955,7 +1020,7 @@ pointInterval: {{$.Statistics.CandleInterval.Duration.Milliseconds}}, name: {{.Name}}, data: [ - {{ range .DataPoints }} + {{ range .LinePlots }} [{{.UnixMilli}},{{.Value}}], {{end}} ] diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index bee7051e464..b85abe92a93 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -787,9 +787,7 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque Fee: calc.Fee, Direction: calc.CurrentDirection, } - if len(calc.PNLHistory) > 3 { - response.UnrealisedPNL = decimal.NewFromInt(-9999999) - } + return response, nil } From 1cd629add4906d3b3911b637cf2b5f35c8e80c5c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 31 Mar 2022 15:56:41 +1100 Subject: [PATCH 114/171] Fixes somet tests,allows for zero fee value v nil distinction,New tests --- backtester/config/config_test.go | 104 +++++++++--------- backtester/config/config_types.go | 4 +- backtester/data/data_test.go | 9 +- backtester/engine/backtest_test.go | 20 ++-- backtester/engine/setup.go | 18 +-- .../eventhandlers/exchange/exchange_test.go | 6 +- .../eventhandlers/portfolio/size/size_test.go | 2 +- .../statistics/currencystatistics_test.go | 2 +- 8 files changed, 87 insertions(+), 78 deletions(-) diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 33fec7a1830..7ecc1c435a9 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -113,8 +113,8 @@ func TestPrintSettings(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -175,8 +175,8 @@ func TestGenerateConfigForDCAAPICandles(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -243,8 +243,8 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -253,8 +253,8 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -311,8 +311,8 @@ func TestGenerateConfigForDCAAPITrades(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, SkipCandleVolumeFitting: true, }, }, @@ -378,8 +378,8 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -391,8 +391,8 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -450,8 +450,8 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -463,8 +463,8 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -522,8 +522,8 @@ func TestGenerateConfigForDCALiveCandles(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -588,8 +588,8 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -602,8 +602,8 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -662,8 +662,8 @@ func TestGenerateConfigForDCACSVCandles(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -718,8 +718,8 @@ func TestGenerateConfigForDCACSVTrades(t *testing.T) { SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -772,8 +772,8 @@ func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { }, BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -860,8 +860,8 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -870,8 +870,8 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -880,8 +880,8 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.BTC.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -890,8 +890,8 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.BTC.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -900,8 +900,8 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.USDT.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: testExchange, @@ -910,8 +910,8 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { Quote: currency.BTC.String(), BuySide: minMax, SellSide: minMax, - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ @@ -997,8 +997,8 @@ func TestValidateCurrencySettings(t *testing.T) { } c.CurrencySettings = append(c.CurrencySettings, CurrencySettings{}) err = c.validateCurrencySettings() - if !errors.Is(err, errNoCurrencySettings) { - t.Errorf("received: %v, expected: %v", err, errNoCurrencySettings) + if !errors.Is(err, errUnsetCurrency) { + t.Errorf("received: %v, expected: %v", err, errUnsetCurrency) } leet := decimal.NewFromInt(1337) c.CurrencySettings[0].SpotDetails = &SpotDetails{InitialQuoteFunds: &leet} @@ -1153,14 +1153,14 @@ func TestValidateStrategySettings(t *testing.T) { c.StrategySettings.SimultaneousSignalProcessing = true err = c.validateStrategySettings() - if !errors.Is(err, errExchangeLevelFundingDataRequired) { - t.Errorf("received %v expected %v", err, errExchangeLevelFundingDataRequired) + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) } c.FundingSettings = FundingSettings{} c.FundingSettings.UseExchangeLevelFunding = true err = c.validateStrategySettings() - if !errors.Is(err, errSimultaneousProcessingRequired) { - t.Errorf("received %v expected %v", err, errSimultaneousProcessingRequired) + if !errors.Is(err, errExchangeLevelFundingDataRequired) { + t.Errorf("received %v expected %v", err, errExchangeLevelFundingDataRequired) } c.FundingSettings.ExchangeLevelFunding = []ExchangeLevelFunding{ { @@ -1230,16 +1230,16 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { Asset: asset.Futures.String(), Base: "BTC", Quote: "20210924", - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, { ExchangeName: "ftx", Asset: asset.Spot.String(), Base: "BTC", Quote: "USD", - MakerFee: makerFee, - TakerFee: takerFee, + MakerFee: &makerFee, + TakerFee: &takerFee, }, }, DataSettings: DataSettings{ diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index eab2f26ba2f..6639f6eea44 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -139,8 +139,8 @@ type CurrencySettings struct { MinimumSlippagePercent decimal.Decimal `json:"min-slippage-percent"` MaximumSlippagePercent decimal.Decimal `json:"max-slippage-percent"` - MakerFee decimal.Decimal `json:"maker-fee-override"` - TakerFee decimal.Decimal `json:"taker-fee-override"` + MakerFee *decimal.Decimal `json:"maker-fee-override,omitempty"` + TakerFee *decimal.Decimal `json:"taker-fee-override,omitempty"` MaximumHoldingsRatio decimal.Decimal `json:"maximum-holdings-ratio"` SkipCandleVolumeFitting bool `json:"skip-candle-volume-fitting"` diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index a68b7d5911c..dc1e6e4ff82 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -5,6 +5,8 @@ import ( "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" @@ -169,7 +171,12 @@ func TestGetDataForCurrency(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d.SetDataForCurrency(exch, a, p, nil) d.SetDataForCurrency(exch, a, currency.NewPair(currency.BTC, currency.DOGE), nil) - result := d.GetDataForCurrency(exch, a, p) + ev := &order.Order{Base: event.Base{ + Exchange: exch, + AssetType: a, + CurrencyPair: p, + }} + result := d.GetDataForCurrency(ev) if result != nil { t.Error("expected nil") } diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index fdd39481249..3f154fe49ba 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -103,8 +103,8 @@ func TestNewFromConfig(t *testing.T) { t.Errorf("received: %v, expected: %v", err, errIntervalUnset) } cfg.DataSettings.Interval = gctkline.OneMin.Duration() - cfg.CurrencySettings[0].MakerFee = decimal.Zero - cfg.CurrencySettings[0].TakerFee = decimal.Zero + cfg.CurrencySettings[0].MakerFee = &decimal.Zero + cfg.CurrencySettings[0].TakerFee = &decimal.Zero _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, gctcommon.ErrDateUnset) { t.Errorf("received: %v, expected: %v", err, gctcommon.ErrDateUnset) @@ -137,8 +137,8 @@ func TestLoadDataAPI(t *testing.T) { }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + MakerFee: &decimal.Zero, + TakerFee: &decimal.Zero, }, }, DataSettings: config.DataSettings{ @@ -194,8 +194,8 @@ func TestLoadDataDatabase(t *testing.T) { }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + MakerFee: &decimal.Zero, + TakerFee: &decimal.Zero, }, }, DataSettings: config.DataSettings{ @@ -262,8 +262,8 @@ func TestLoadDataCSV(t *testing.T) { }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + MakerFee: &decimal.Zero, + TakerFee: &decimal.Zero, }, }, DataSettings: config.DataSettings{ @@ -320,8 +320,8 @@ func TestLoadDataLive(t *testing.T) { }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, + MakerFee: &decimal.Zero, + TakerFee: &decimal.Zero, }, }, DataSettings: config.DataSettings{ diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index 21eb55ae353..c5c037123a2 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -234,7 +234,9 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool } portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet - if cfg.CurrencySettings[i].MakerFee.GreaterThan(cfg.CurrencySettings[i].TakerFee) { + if cfg.CurrencySettings[i].MakerFee != nil && + cfg.CurrencySettings[i].TakerFee != nil && + cfg.CurrencySettings[i].MakerFee.GreaterThan(*cfg.CurrencySettings[i].TakerFee) { log.Warnf(common.SubLoggers[common.Setup], "maker fee '%v' should not exceed taker fee '%v'. Please review config", cfg.CurrencySettings[i].MakerFee, cfg.CurrencySettings[i].TakerFee) @@ -457,19 +459,19 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange bt.Datas.SetDataForCurrency(exchangeName, a, pair, klineData) var makerFee, takerFee decimal.Decimal - if cfg.CurrencySettings[i].MakerFee.GreaterThan(decimal.Zero) { - makerFee = cfg.CurrencySettings[i].MakerFee + if cfg.CurrencySettings[i].MakerFee != nil && cfg.CurrencySettings[i].MakerFee.GreaterThan(decimal.Zero) { + makerFee = *cfg.CurrencySettings[i].MakerFee } - if cfg.CurrencySettings[i].TakerFee.GreaterThan(decimal.Zero) { - takerFee = cfg.CurrencySettings[i].TakerFee + if cfg.CurrencySettings[i].TakerFee != nil && cfg.CurrencySettings[i].TakerFee.GreaterThan(decimal.Zero) { + takerFee = *cfg.CurrencySettings[i].TakerFee } - if makerFee.IsZero() || takerFee.IsZero() { + if cfg.CurrencySettings[i].TakerFee == nil || cfg.CurrencySettings[i].MakerFee == nil { var apiMakerFee, apiTakerFee decimal.Decimal apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair) - if makerFee.IsZero() { + if cfg.CurrencySettings[i].MakerFee == nil { makerFee = apiMakerFee } - if takerFee.IsZero() { + if cfg.CurrencySettings[i].TakerFee == nil { takerFee = apiTakerFee } } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 9d8fe2cfa21..e35763d94dc 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -166,7 +166,7 @@ func TestPlaceOrder(t *testing.T) { } em.Add(exch) bot.ExchangeManager = em - bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false,false) + bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false) if err != nil { t.Error(err) } @@ -224,7 +224,7 @@ func TestExecuteOrder(t *testing.T) { } em.Add(exch) bot.ExchangeManager = em - bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, ,false,false) + bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false) if err != nil { t.Error(err) } @@ -324,7 +324,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { em.Add(exch) bot.ExchangeManager = em - bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false,false) + bot.OrderManager, err = engine.SetupOrderManager(em, &engine.CommunicationManager{}, &bot.ServicesWG, false, false) if err != nil { t.Error(err) } diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index 8787e90cd58..e0f2c601ef0 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -200,7 +200,7 @@ func TestSizeOrder(t *testing.T) { } o.Direction = gctorder.Buy - o.Price = decimal.NewFromInt(1) + o.ClosePrice = decimal.NewFromInt(1) s.BuySide.MaximumSize = decimal.NewFromInt(1) s.BuySide.MinimumSize = decimal.NewFromInt(1) _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index eb934385300..8a52b7bc144 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -117,7 +117,7 @@ func TestCalculateResults(t *testing.T) { t.Error(err) } if !cs.MarketMovement.Equal(decimal.NewFromFloat(-33.15)) { - t.Error("expected -33.15") + t.Errorf("expected -33.15 received '%v'", cs.MarketMovement) } ev3 := ev2 ev3.DataEvent = &kline.Kline{ From 423ce6a6957fa7ed46e27a990e0fa1ab9320410e Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 1 Apr 2022 17:00:06 +1100 Subject: [PATCH 115/171] Some annoying test fixes that took too long --- backtester/config/configbuilder/main.go | 52 +++++++++++-------- .../eventhandlers/portfolio/portfolio_test.go | 3 +- .../eventhandlers/portfolio/size/size.go | 4 +- .../eventhandlers/portfolio/size/size_test.go | 14 +++-- .../statistics/currencystatistics.go | 3 +- .../statistics/currencystatistics_test.go | 10 +++- .../statistics/statistics_test.go | 8 +++ .../ftxcashandcarry/ftxcashandcarry_test.go | 4 +- 8 files changed, 66 insertions(+), 32 deletions(-) diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 4a60c1e2681..cb0beddfec7 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -46,10 +46,12 @@ func main() { StrategySettings: config.StrategySettings{ Name: "", SimultaneousSignalProcessing: false, - UseExchangeLevelFunding: false, - ExchangeLevelFunding: nil, CustomSettings: nil, }, + FundingSettings: config.FundingSettings{ + UseExchangeLevelFunding: false, + ExchangeLevelFunding: nil, + }, CurrencySettings: []config.CurrencySettings{}, DataSettings: config.DataSettings{ Interval: 0, @@ -219,7 +221,7 @@ func parseExchangeSettings(reader *bufio.Reader, cfg *config.Config) error { addCurrency := y for strings.Contains(addCurrency, y) { var currencySetting *config.CurrencySettings - currencySetting, err = addCurrencySetting(reader, cfg.StrategySettings.UseExchangeLevelFunding) + currencySetting, err = addCurrencySetting(reader, cfg.FundingSettings.UseExchangeLevelFunding) if err != nil { return err } @@ -266,8 +268,8 @@ func parseStrategySettings(cfg *config.Config, reader *bufio.Reader) error { } fmt.Println("Will this strategy be able to share funds at an exchange level? y/n") yn = quickParse(reader) - cfg.StrategySettings.UseExchangeLevelFunding = strings.Contains(yn, y) - if !cfg.StrategySettings.UseExchangeLevelFunding { + cfg.FundingSettings.UseExchangeLevelFunding = strings.Contains(yn, y) + if !cfg.FundingSettings.UseExchangeLevelFunding { return nil } @@ -317,7 +319,7 @@ func parseStrategySettings(cfg *config.Config, reader *bufio.Reader) error { return err } } - cfg.StrategySettings.ExchangeLevelFunding = append(cfg.StrategySettings.ExchangeLevelFunding, fund) + cfg.FundingSettings.ExchangeLevelFunding = append(cfg.FundingSettings.ExchangeLevelFunding, fund) fmt.Println("Add another source of funds? y/n") addFunding = quickParse(reader) } @@ -622,27 +624,33 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* case asset.Futures.String(): } - fmt.Println("Enter the maker-fee. eg 0.001") - parseNum := quickParse(reader) - if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) - if err != nil { - return nil, err + fmt.Println("Do you want to set custom fees? If no, Backtester will use default fees for exchange y/n") + yn := quickParse(reader) + if yn == y || yn == yes { + fmt.Println("Enter the maker-fee. eg 0.001") + parseNum := quickParse(reader) + if parseNum != "" { + f, err = strconv.ParseFloat(parseNum, 64) + if err != nil { + return nil, err + } + d := decimal.NewFromFloat(f) + setting.MakerFee = &d } - setting.MakerFee = decimal.NewFromFloat(f) - } - fmt.Println("Enter the taker-fee. eg 0.01") - parseNum = quickParse(reader) - if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) - if err != nil { - return nil, err + fmt.Println("Enter the taker-fee. eg 0.01") + parseNum = quickParse(reader) + if parseNum != "" { + f, err = strconv.ParseFloat(parseNum, 64) + if err != nil { + return nil, err + } + d := decimal.NewFromFloat(f) + setting.TakerFee = &d } - setting.TakerFee = decimal.NewFromFloat(f) } fmt.Println("Will there be buy-side limits? y/n") - yn := quickParse(reader) + yn = quickParse(reader) if yn == y || yn == yes { setting.BuySide, err = minMaxParse("buy", reader) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index d1018b01f9c..d650c21b223 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -965,7 +965,7 @@ func TestGetLatestPNLForEvent(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } - expectedError = nil + expectedError = gctorder.ErrPositionsNotLoadedForPair _, err = p.GetLatestPNLForEvent(ev) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) @@ -975,6 +975,7 @@ func TestGetLatestPNLForEvent(t *testing.T) { if !ok { t.Fatalf("where did settings go?") } + expectedError = nil err = settings.FuturesTracker.TrackNewOrder(&gctorder.Detail{ Exchange: ev.GetExchange(), AssetType: ev.AssetType, diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index d19d1c2f9cc..eab884946ea 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -58,10 +58,12 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if amount.GreaterThan(portfolioSize) { amount = portfolioSize } + default: + return retOrder, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } // amount = amount.Round(8) if amount.LessThanOrEqual(decimal.Zero) { - return retOrder, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) + return retOrder, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } retOrder.SetAmount(amount) diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index e0f2c601ef0..d0bd7a49c6e 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -190,16 +190,15 @@ func TestSizeOrder(t *testing.T) { } _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) - if !errors.Is(err, errNoFunds) { - t.Errorf("received: %v, expected: %v", err, errNoFunds) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received: %v, expected: %v", err, errCannotAllocate) } - o.Amount = decimal.NewFromInt(1) + o.Direction = gctorder.Buy _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) } - o.Direction = gctorder.Buy o.ClosePrice = decimal.NewFromInt(1) s.BuySide.MaximumSize = decimal.NewFromInt(1) s.BuySide.MinimumSize = decimal.NewFromInt(1) @@ -208,6 +207,7 @@ func TestSizeOrder(t *testing.T) { t.Error(err) } + o.Amount = decimal.NewFromInt(1) o.Direction = gctorder.Sell _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { @@ -220,4 +220,10 @@ func TestSizeOrder(t *testing.T) { if err != nil { t.Error(err) } + + o.Direction = common.ClosePosition + _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + if err != nil { + t.Error(err) + } } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 22cff608a26..fd1d27e924a 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -132,9 +132,10 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() { for i := range c.Events { - if c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice).GreaterThan(c.HighestCommittedFunds.Value) { + if c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice).GreaterThan(c.HighestCommittedFunds.Value) || !c.HighestCommittedFunds.Set { c.HighestCommittedFunds.Value = c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice) c.HighestCommittedFunds.Time = c.Events[i].Holdings.Timestamp + c.HighestCommittedFunds.Set = true } } } diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 8a52b7bc144..6e605df5cea 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -32,8 +32,12 @@ func TestCalculateResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, + Offset: 1, } ev := DataAtOffset{ + Offset: 1, + Time: tt1, + ClosePrice: decimal.NewFromInt(2000), Holdings: holdings.Holding{ ChangeInTotalValuePercent: decimal.NewFromFloat(0.1333), Timestamp: tt1, @@ -72,7 +76,11 @@ func TestCalculateResults(t *testing.T) { } even2 := even even2.Time = tt2 + even2.Offset = 2 ev2 := DataAtOffset{ + Offset: 2, + Time: tt2, + ClosePrice: decimal.NewFromInt(1337), Holdings: holdings.Holding{ ChangeInTotalValuePercent: decimal.NewFromFloat(0.1337), Timestamp: tt2, @@ -267,7 +275,7 @@ func TestCalculateHighestCommittedFunds(t *testing.T) { DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1339)}, Holdings: holdings.Holding{Timestamp: tt3, BaseSize: decimal.NewFromInt(11)}}, ) c.calculateHighestCommittedFunds() - if c.HighestCommittedFunds.Time != tt2 { + if c.HighestCommittedFunds.Time != tt1 { t.Errorf("expected %v, received %v", tt2, c.HighestCommittedFunds.Time) } } diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 15f1c1db2d8..5df41d225d7 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -596,6 +596,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, + Offset: 1, }, Open: eleet, Close: eleet, @@ -613,6 +614,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, + Offset: 2, }, OpenPrice: eleet, HighPrice: eleet, @@ -631,6 +633,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, + Offset: 3, }, Open: eleeb, Close: eleeb, @@ -649,6 +652,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, + Offset: 4, }, OpenPrice: eleet, HighPrice: eleet, @@ -668,6 +672,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, + Offset: 5, }, Open: eleeb, Close: eleeb, @@ -685,6 +690,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, + Offset: 6, }, OpenPrice: eleeb, HighPrice: eleeb, @@ -704,6 +710,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, + Offset: 7, }, Open: eleeb, Close: eleeb, @@ -721,6 +728,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, + Offset: 8, }, OpenPrice: eleeb, HighPrice: eleeb, diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 8743c5fcce7..ec501a17022 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -196,10 +196,10 @@ func TestSetDefaults(t *testing.T) { t.Parallel() s := Strategy{} s.SetDefaults() - if !s.openShortDistancePercentage.Equal(decimal.NewFromInt(5)) { + if !s.openShortDistancePercentage.Equal(decimal.NewFromInt(0)) { t.Errorf("expected 5, received %v", s.openShortDistancePercentage) } - if !s.closeShortDistancePercentage.Equal(decimal.NewFromInt(5)) { + if !s.closeShortDistancePercentage.Equal(decimal.NewFromInt(0)) { t.Errorf("expected 5, received %v", s.closeShortDistancePercentage) } } From a9bba1c99cccde550de0f23deebcb90f282a951a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 4 Apr 2022 13:43:51 +1000 Subject: [PATCH 116/171] portfolio coverage --- .../portfolio/holdings/holdings.go | 10 -- .../eventhandlers/portfolio/portfolio.go | 8 +- .../eventhandlers/portfolio/portfolio_test.go | 98 +++++++++++++++++++ .../statistics/statistics_test.go | 14 +-- 4 files changed, 112 insertions(+), 18 deletions(-) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index 01676c16439..bf2243650c2 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -74,16 +74,6 @@ func (h *Holding) UpdateValue(d common.DataEventHandler) { h.updateValue(latest) } -// HasInvestments determines whether there are any holdings in the base funds -func (h *Holding) HasInvestments() bool { - return h.BaseSize.GreaterThan(decimal.Zero) -} - -// HasFunds determines whether there are any holdings in the quote funds -func (h *Holding) HasFunds() bool { - return h.QuoteSize.GreaterThan(decimal.Zero) -} - func (h *Holding) update(e fill.Event, f funding.IPairReader) { direction := e.GetDirection() if o := e.GetOrder(); o != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 8e9d60146c1..9aa124daae6 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -630,9 +630,15 @@ func (p *Portfolio) CheckLiquidationStatus(ev common.DataEventHandler, collatera } func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandler, funds funding.IFundingManager) ([]order.Event, error) { + if ev == nil { + return nil, common.ErrNilEvent + } + if funds == nil { + return nil, fmt.Errorf("%w, requires funding manager", common.ErrNilArguments) + } var closingOrders []order.Event for exch, assetMap := range p.exchangeAssetPairSettings { - if exch != ev.GetExchange() { + if !strings.EqualFold(ev.GetExchange(), exch) { // only liquidate the same exchange continue } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index d650c21b223..59c5c7f8c14 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -1295,3 +1295,101 @@ func TestCannotPurchase(t *testing.T) { t.Errorf("received '%v' expected '%v'", result.Direction, common.DoNothing) } } + +func TestCreateLiquidationOrdersForExchange(t *testing.T) { + t.Parallel() + + p := &Portfolio{} + var expectedError = common.ErrNilEvent + _, err := p.CreateLiquidationOrdersForExchange(nil, nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + ev := &kline.Kline{} + expectedError = common.ErrNilArguments + _, err = p.CreateLiquidationOrdersForExchange(ev, nil) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + funds := &funding.FundManager{} + expectedError = nil + _, err = p.CreateLiquidationOrdersForExchange(ev, funds) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + ff := &ftx.FTX{} + ff.Name = testExchange + cp := currency.NewPair(currency.BTC, currency.USD) + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Futures, Pair: cp}) + if err != nil { + t.Error(err) + } + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Spot, Pair: cp}) + if err != nil { + t.Error(err) + } + _, err = p.CreateLiquidationOrdersForExchange(ev, funds) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + settings, err := p.getSettings(ff.Name, asset.Futures, cp) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + err = settings.FuturesTracker.TrackNewOrder(&gctorder.Detail{ + Exchange: ff.Name, + AssetType: asset.Futures, + Pair: cp, + Side: gctorder.Long, + ID: "lol", + Date: time.Now(), + Amount: 1337, + Price: 1337, + }) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + ev.Exchange = ff.Name + ev.AssetType = asset.Futures + ev.CurrencyPair = cp + _, err = p.CreateLiquidationOrdersForExchange(ev, funds) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + + // spot order + item, err := funding.CreateItem(ff.Name, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + err = funds.AddItem(item) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + item.IncreaseAvailable(decimal.NewFromInt(1337)) + orders, err := p.CreateLiquidationOrdersForExchange(ev, funds) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } + if len(orders) != 2 { + t.Errorf("expected two orders generated, received '%v'", len(orders)) + } +} + +func TestGetPositionStatus(t *testing.T) { + t.Parallel() + p := PNLSummary{ + Result: gctorder.PNLResult{ + Status: gctorder.Rejected, + }, + } + status := p.GetPositionStatus() + if gctorder.Rejected != status { + t.Errorf("expected '%v' received '%v'", gctorder.Rejected, status) + } +} diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 5df41d225d7..a9214e2a196 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -614,7 +614,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, - Offset: 2, + Offset: 1, }, OpenPrice: eleet, HighPrice: eleet, @@ -633,7 +633,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, - Offset: 3, + Offset: 2, }, Open: eleeb, Close: eleeb, @@ -652,7 +652,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, - Offset: 4, + Offset: 2, }, OpenPrice: eleet, HighPrice: eleet, @@ -672,7 +672,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, - Offset: 5, + Offset: 3, }, Open: eleeb, Close: eleeb, @@ -690,7 +690,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p, AssetType: a, - Offset: 6, + Offset: 3, }, OpenPrice: eleeb, HighPrice: eleeb, @@ -710,7 +710,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, - Offset: 7, + Offset: 4, }, Open: eleeb, Close: eleeb, @@ -728,7 +728,7 @@ func TestCalculateTheResults(t *testing.T) { Interval: gctkline.OneDay, CurrencyPair: p2, AssetType: a, - Offset: 8, + Offset: 4, }, OpenPrice: eleeb, HighPrice: eleeb, From c87febe7db0bae48074a16b0d9e1e1010a9bb8b3 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 4 Apr 2022 16:23:14 +1000 Subject: [PATCH 117/171] holding coverage, privatisation funding --- backtester/engine/backtest.go | 6 +++ backtester/engine/backtest_test.go | 40 ++++++++++++++ .../eventhandlers/exchange/exchange_test.go | 14 ++--- .../portfolio/holdings/holdings_test.go | 24 +++++++++ .../eventhandlers/portfolio/portfolio_test.go | 27 ++++++---- .../ftxcashandcarry/ftxcashandcarry.go | 4 +- backtester/funding/collateral.go | 50 +++++++++--------- backtester/funding/funding.go | 52 +++++++++++++------ backtester/funding/funding_test.go | 48 ++++++++--------- backtester/funding/funding_types.go | 51 +++++++++--------- backtester/funding/pair.go | 44 ++++++++-------- 11 files changed, 228 insertions(+), 132 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index a7881964c10..a6772d20dd5 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -268,6 +268,12 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } func (bt *BackTest) triggerLiquidationsForExchange(ev common.DataEventHandler, pnl *portfolio.PNLSummary) error { + if ev == nil { + return common.ErrNilEvent + } + if pnl == nil { + return fmt.Errorf("%w pnl summary", common.ErrNilArguments) + } orders, err := bt.Portfolio.CreateLiquidationOrdersForExchange(ev, bt.Funding) if err != nil { return err diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 3f154fe49ba..250f79764c5 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -21,6 +21,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/dollarcostaverage" + evkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/backtester/report" gctcommon "github.com/thrasher-corp/gocryptotrader/common" @@ -660,3 +662,41 @@ func TestFullCycleMulti(t *testing.T) { t.Error(err) } } + +type portfolioOverride struct { + Err error + portfolio.Portfolio +} + +func (p portfolioOverride) CreateLiquidationOrdersForExchange(common.DataEventHandler, funding.IFundingManager) ([]order.Event, error) { + if p.Err != nil { + return nil, p.Err + } + return []order.Event{ + &order.Order{}, + }, nil +} + +func TestTriggerLiquidationsForExchange(t *testing.T) { + t.Parallel() + bt := BackTest{} + expectedError := common.ErrNilEvent + err := bt.triggerLiquidationsForExchange(nil, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + expectedError = common.ErrNilArguments + ev := &evkline.Kline{} + err = bt.triggerLiquidationsForExchange(ev, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + bt.Portfolio = &portfolioOverride{} + pnl := &portfolio.PNLSummary{} + err = bt.triggerLiquidationsForExchange(ev, pnl) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } +} diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index e35763d94dc..8f57fced281 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -605,11 +605,10 @@ func TestAllocateFundsPostOrder(t *testing.T) { if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } - fundPair := &funding.Pair{ - Base: item, - Quote: item2, + fundPair, err := funding.CreatePair(item, item2) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) } - expectedError = nil err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) @@ -646,10 +645,11 @@ func TestAllocateFundsPostOrder(t *testing.T) { if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } - collateralPair := &funding.Collateral{ - Contract: item3, - Collateral: item4, + collateralPair, err := funding.CreateCollateral(item, item2) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) } + expectedError = gctorder.ErrSubmissionIsNil err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one) if !errors.Is(err, expectedError) { diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index e4a6097e041..f9a6e4dba81 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -38,6 +38,23 @@ func pair(t *testing.T) *funding.Pair { return p } +func collateral(t *testing.T) *funding.Collateral { + t.Helper() + b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero) + if err != nil { + t.Fatal(err) + } + q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) + if err != nil { + t.Fatal(err) + } + p, err := funding.CreateCollateral(b, q) + if err != nil { + t.Fatal(err) + } + return p +} + func TestCreate(t *testing.T) { t.Parallel() _, err := Create(nil, pair(t)) @@ -50,6 +67,13 @@ func TestCreate(t *testing.T) { if err != nil { t.Error(err) } + + _, err = Create(&fill.Fill{ + Base: event.Base{AssetType: asset.Futures}, + }, collateral(t)) + if err != nil { + t.Error(err) + } } func TestUpdate(t *testing.T) { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 59c5c7f8c14..766bd3ee4f4 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -846,11 +846,23 @@ func TestTrackFuturesOrder(t *testing.T) { t.Errorf("received '%v' expected '%v", err, expectedError) } - fundCollateral := &funding.Collateral{} + expectedError = nil + contract, err := funding.CreateItem(od.Exchange, od.AssetType, od.Pair.Base, decimal.NewFromInt(100), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + collateral, err := funding.CreateItem(od.Exchange, od.AssetType, od.Pair.Quote, decimal.NewFromInt(100), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + collat, err := funding.CreateCollateral(contract, collateral) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } expectedError = errExchangeUnset _, err = p.TrackFuturesOrder(&fill.Fill{ Order: od, - }, fundCollateral) + }, collat) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v", err, expectedError) } @@ -871,14 +883,7 @@ func TestTrackFuturesOrder(t *testing.T) { od.ID = testExchange od.Date = time.Now() expectedError = nil - fundCollateral.Collateral, err = funding.CreateItem(od.Exchange, od.AssetType, od.Pair.Base, decimal.NewFromInt(100), decimal.Zero) - if !errors.Is(err, expectedError) { - t.Errorf("received '%v' expected '%v", err, expectedError) - } - fundCollateral.Contract, err = funding.CreateItem(od.Exchange, od.AssetType, od.Pair.Quote, decimal.NewFromInt(100), decimal.Zero) - if !errors.Is(err, expectedError) { - t.Errorf("received '%v' expected '%v", err, expectedError) - } + _, err = p.TrackFuturesOrder(&fill.Fill{ Order: od, Base: event.Base{ @@ -886,7 +891,7 @@ func TestTrackFuturesOrder(t *testing.T) { AssetType: asset.Futures, CurrencyPair: cp, }, - }, fundCollateral) + }, collat) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v", err, expectedError) } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 54d480df514..67b21f3fdce 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -78,9 +78,9 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra sp := v.spotSignal.Latest().GetClosePrice() hundred := decimal.NewFromInt(100) diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(hundred) - futuresSignal.AppendReasonf("Difference %v", diffBetweenFuturesSpot) + futuresSignal.AppendReasonf("Futures Spot Difference: %v%%", diffBetweenFuturesSpot) if pos != nil && pos[len(pos)-1].Status == order.Open { - futuresSignal.AppendReasonf("Unrealised PNL %v", pos[len(pos)-1].UnrealisedPNL) + futuresSignal.AppendReasonf("Unrealised PNL: %v %v", pos[len(pos)-1].UnrealisedPNL, pos[len(pos)-1].Underlying) } if f.HasExchangeBeenLiquidated(&spotSignal) || f.HasExchangeBeenLiquidated(&futuresSignal) { spotSignal.AppendReason("cannot transact, has been liquidated") diff --git a/backtester/funding/collateral.go b/backtester/funding/collateral.go index 0a23ea9c276..483468c4c78 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateral.go @@ -20,41 +20,41 @@ var ( // CanPlaceOrder checks if there is any collateral to spare func (c *Collateral) CanPlaceOrder(_ gctorder.Side) bool { - return c.Collateral.CanPlaceOrder() + return c.collateral.CanPlaceOrder() } // TakeProfit handles both the reduction of contracts and the change in collateral func (c *Collateral) TakeProfit(contracts, positionReturns decimal.Decimal) error { - err := c.Contract.ReduceContracts(contracts) + err := c.contract.ReduceContracts(contracts) if err != nil { return err } - return c.Collateral.TakeProfit(positionReturns) + return c.collateral.TakeProfit(positionReturns) } // ContractCurrency returns the contract currency func (c *Collateral) ContractCurrency() currency.Code { - return c.Contract.currency + return c.contract.currency } // CollateralCurrency returns collateral currency func (c *Collateral) CollateralCurrency() currency.Code { - return c.Collateral.currency + return c.collateral.currency } // InitialFunds returns initial funds of collateral func (c *Collateral) InitialFunds() decimal.Decimal { - return c.Collateral.initialFunds + return c.collateral.initialFunds } // AvailableFunds returns available funds of collateral func (c *Collateral) AvailableFunds() decimal.Decimal { - return c.Collateral.available + return c.collateral.available } // GetPairReader returns an error because collateral isn't a pair func (c *Collateral) GetPairReader() (IPairReader, error) { - return nil, fmt.Errorf("could not return pair reader for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) + return nil, fmt.Errorf("could not return pair reader for %v %v %v %v %w", c.contract.exchange, c.collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } // GetCollateralReader returns a collateral reader interface of Collateral @@ -67,11 +67,11 @@ func (c *Collateral) UpdateContracts(s gctorder.Side, amount decimal.Decimal) er switch { case c.currentDirection == nil: c.currentDirection = &s - return c.Contract.AddContracts(amount) + return c.contract.AddContracts(amount) case *c.currentDirection == s: - return c.Contract.AddContracts(amount) + return c.contract.AddContracts(amount) case *c.currentDirection != s: - return c.Contract.ReduceContracts(amount) + return c.contract.ReduceContracts(amount) default: return errors.New("woah nelly") } @@ -82,10 +82,10 @@ func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { if amount.LessThan(decimal.Zero) { return fmt.Errorf("release %w", errPositiveOnly) } - if c.Contract.available.LessThan(amount) { - return fmt.Errorf("%w amount '%v' larger than available '%v'", errCannotAllocate, amount, c.Contract.available) + if c.contract.available.LessThan(amount) { + return fmt.Errorf("%w amount '%v' larger than available '%v'", errCannotAllocate, amount, c.contract.available) } - c.Contract.available = c.Contract.available.Sub(amount) + c.contract.available = c.contract.available.Sub(amount) return nil } @@ -101,22 +101,22 @@ func (c *Collateral) FundReserver() IFundReserver { // PairReleaser returns an error as there is no such thing for collateral func (c *Collateral) PairReleaser() (IPairReleaser, error) { - return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.Contract.exchange, c.Collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) + return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.contract.exchange, c.collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } // Reserve reserves or releases collateral based on order side func (c *Collateral) Reserve(amount decimal.Decimal, side gctorder.Side) error { switch side { case gctorder.Long, gctorder.Short: - return c.Collateral.Reserve(amount) + return c.collateral.Reserve(amount) case common.ClosePosition: - return c.Collateral.Release(amount, amount) + return c.collateral.Release(amount, amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", errCannotAllocate, - c.Collateral.exchange, - c.Collateral.asset, - c.Collateral.currency, + c.collateral.exchange, + c.collateral.asset, + c.collateral.currency, side) } } @@ -135,14 +135,14 @@ func (c *Collateral) FundReleaser() IFundReleaser { // Liquidate kills your funds and future func (c *Collateral) Liquidate() { - c.Collateral.available = decimal.Zero - c.Collateral.reserved = decimal.Zero - c.Contract.available = decimal.Zero - c.Contract.reserved = decimal.Zero + c.collateral.available = decimal.Zero + c.collateral.reserved = decimal.Zero + c.contract.available = decimal.Zero + c.contract.reserved = decimal.Zero c.currentDirection = nil } // CurrentHoldings returns available contract holdings func (c *Collateral) CurrentHoldings() decimal.Decimal { - return c.Contract.available + return c.contract.available } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index e106e9f3646..9172d1ec107 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -266,7 +266,27 @@ func CreatePair(base, quote *Item) (*Pair, error) { qCopy := *quote bCopy.pairedWith = &qCopy qCopy.pairedWith = &bCopy - return &Pair{Base: &bCopy, Quote: &qCopy}, nil + return &Pair{base: &bCopy, quote: &qCopy}, nil +} + +// CreateCollateral adds two funding items and associates them with one another +// the association allows for the same currency to be used multiple times when +// usingExchangeLevelFunding is false. eg BTC-USDT and LTC-USDT do not share the same +// USDT level funding +func CreateCollateral(contract, collateral *Item) (*Collateral, error) { + if contract == nil { + return nil, fmt.Errorf("base %w", common.ErrNilArguments) + } + if collateral == nil { + return nil, fmt.Errorf("quote %w", common.ErrNilArguments) + } + // copy to prevent the off chance of sending in the same base OR quote + // to create a new pair with a new base OR quote + bCopy := *contract + qCopy := *collateral + bCopy.pairedWith = &qCopy + qCopy.pairedWith = &bCopy + return &Collateral{contract: &bCopy, collateral: &qCopy}, nil } // Reset clears all settings @@ -315,7 +335,7 @@ func (f *FundManager) GenerateReport() *Report { for j := range report.USDTotalsOverTime { if report.USDTotalsOverTime[j].Time.Equal(v.Time) { report.USDTotalsOverTime[j].USDValue = report.USDTotalsOverTime[j].USDValue.Add(v.USDValue) - report.USDTotalsOverTime[j].Breakdown = append(report.USDTotalsOverTime[j].Breakdown, Thing{ + report.USDTotalsOverTime[j].Breakdown = append(report.USDTotalsOverTime[j].Breakdown, CurrencyContribution{ Currency: f.items[i].currency, USD: v.USDValue, }) @@ -327,7 +347,7 @@ func (f *FundManager) GenerateReport() *Report { report.USDTotalsOverTime = append(report.USDTotalsOverTime, ItemSnapshot{ Time: v.Time, USDValue: v.USDValue, - Breakdown: []Thing{ + Breakdown: []CurrencyContribution{ { Currency: f.items[i].currency, USD: v.USDValue, @@ -426,13 +446,13 @@ func (f *FundManager) Exists(item *Item) bool { // AddPair adds a pair to the fund manager if it does not exist func (f *FundManager) AddPair(p *Pair) error { - if f.Exists(p.Base) { - return fmt.Errorf("%w %v", ErrAlreadyExists, p.Base) + if f.Exists(p.base) { + return fmt.Errorf("%w %v", ErrAlreadyExists, p.base) } - if f.Exists(p.Quote) { - return fmt.Errorf("%w %v", ErrAlreadyExists, p.Quote) + if f.Exists(p.quote) { + return fmt.Errorf("%w %v", ErrAlreadyExists, p.quote) } - f.items = append(f.items, p.Base, p.Quote) + f.items = append(f.items, p.base, p.quote) return nil } @@ -448,30 +468,30 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair func (f *FundManager) getFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { - var resp Pair - var collat Collateral if a.IsFutures() { + var collat Collateral for i := range f.items { if f.items[i].MatchesCurrency(currency.NewCode(p.String())) { - collat.Contract = f.items[i] - collat.Collateral = f.items[i].pairedWith + collat.contract = f.items[i] + collat.collateral = f.items[i].pairedWith return &collat, nil } } } else { + var resp Pair for i := range f.items { if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { - resp.Base = f.items[i] + resp.base = f.items[i] continue } if f.items[i].BasicEqual(exch, a, p.Quote, p.Base) { - resp.Quote = f.items[i] + resp.quote = f.items[i] } } - if resp.Base == nil { + if resp.base == nil { return nil, fmt.Errorf("base %v %w", p.Base, ErrFundsNotFound) } - if resp.Quote == nil { + if resp.quote == nil { return nil, fmt.Errorf("quote %v %w", p.Quote, ErrFundsNotFound) } return &resp, nil diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 927d0768fef..739ce34301f 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -417,7 +417,7 @@ func TestBaseInitialFunds(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} funds := pairItems.BaseInitialFunds() if !funds.IsZero() { t.Errorf("received '%v' expected '%v'", funds, baseItem.available) @@ -436,7 +436,7 @@ func TestQuoteInitialFunds(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} funds := pairItems.QuoteInitialFunds() if !funds.Equal(elite) { t.Errorf("received '%v' expected '%v'", funds, elite) @@ -455,7 +455,7 @@ func TestBaseAvailable(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} funds := pairItems.BaseAvailable() if !funds.IsZero() { t.Errorf("received '%v' expected '%v'", funds, baseItem.available) @@ -474,7 +474,7 @@ func TestQuoteAvailable(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} funds := pairItems.QuoteAvailable() if !funds.Equal(elite) { t.Errorf("received '%v' expected '%v'", funds, elite) @@ -493,7 +493,7 @@ func TestReservePair(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} err = pairItems.Reserve(decimal.Zero, gctorder.Buy) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) @@ -528,7 +528,7 @@ func TestReleasePair(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} err = pairItems.Reserve(decimal.Zero, gctorder.Buy) if !errors.Is(err, errZeroAmountReceived) { t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) @@ -586,36 +586,36 @@ func TestIncreaseAvailablePair(t *testing.T) { } baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem - pairItems := Pair{Base: baseItem, Quote: quoteItem} + pairItems := Pair{base: baseItem, quote: quoteItem} pairItems.IncreaseAvailable(decimal.Zero, gctorder.Buy) - if !pairItems.Quote.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.Quote.available) + if !pairItems.quote.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) } pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) - if !pairItems.Base.available.IsZero() { - t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.Base.available) + if !pairItems.base.available.IsZero() { + t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.base.available) } pairItems.IncreaseAvailable(elite.Neg(), gctorder.Sell) - if !pairItems.Quote.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.Quote.available) + if !pairItems.quote.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) } pairItems.IncreaseAvailable(elite, gctorder.Buy) - if !pairItems.Base.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.Base.available) + if !pairItems.base.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) } pairItems.IncreaseAvailable(elite, common.DoNothing) - if !pairItems.Base.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.Base.available) + if !pairItems.base.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) } } func TestCanPlaceOrderPair(t *testing.T) { t.Parallel() p := Pair{ - Base: &Item{}, - Quote: &Item{}, + base: &Item{}, + quote: &Item{}, } if p.CanPlaceOrder(common.DoNothing) { t.Error("expected false") @@ -627,11 +627,11 @@ func TestCanPlaceOrderPair(t *testing.T) { t.Error("expected false") } - p.Quote.available = decimal.NewFromInt(32) + p.quote.available = decimal.NewFromInt(32) if !p.CanPlaceOrder(gctorder.Buy) { t.Error("expected true") } - p.Base.available = decimal.NewFromInt(32) + p.base.available = decimal.NewFromInt(32) if !p.CanPlaceOrder(gctorder.Sell) { t.Error("expected true") } @@ -1001,13 +1001,13 @@ func TestCanPlaceOrder(t *testing.T) { t.Parallel() item := &Item{} c := &Collateral{ - Contract: item, - Collateral: item, + contract: item, + collateral: item, } if c.CanPlaceOrder(gctorder.Buy) { t.Error("expected false") } - c.Collateral.available = decimal.NewFromInt(1337) + c.collateral.available = decimal.NewFromInt(1337) if !c.CanPlaceOrder(gctorder.Buy) { t.Error("expected true") } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index a27309fb14a..40e6015e18a 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -12,15 +12,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) -// FundManager is the benevolent holder of all funding levels across all -// currencies used in the backtester -type FundManager struct { - usingExchangeLevelFunding bool - disableUSDTracking bool - items []*Item - exchangeManager *engine.ExchangeManager -} - // IFundingManager limits funding usage for portfolio event handling type IFundingManager interface { Reset() @@ -121,6 +112,15 @@ type ICollateralReleaser interface { Liquidate() } +// FundManager is the benevolent holder of all funding levels across all +// currencies used in the backtester +type FundManager struct { + usingExchangeLevelFunding bool + disableUSDTracking bool + items []*Item + exchangeManager *engine.ExchangeManager +} + // Item holds funding data per currency item type Item struct { exchange string @@ -138,6 +138,20 @@ type Item struct { collateralCandles map[currency.Code]kline.DataFromKline } +// Pair holds two currencies that are associated with each other +type Pair struct { + base *Item + quote *Item +} + +// Collateral consists of a currency pair for a futures contract +// and associates it with an addition collateral pair to take funding from +type Collateral struct { + currentDirection *order.Side + contract *Item + collateral *Item +} + // BasicItem is a representation of Item type BasicItem struct { Exchange string @@ -149,20 +163,6 @@ type BasicItem struct { USDPrice decimal.Decimal } -// Pair holds two currencies that are associated with each other -type Pair struct { - Base *Item - Quote *Item -} - -// Collateral consists of a currency pair for a futures contract -// and associates it with an addition collateral pair to take funding from -type Collateral struct { - currentDirection *order.Side - Contract *Item - Collateral *Item -} - // Report holds all funding data for result reporting type Report struct { DisableUSDTracking bool @@ -198,10 +198,11 @@ type ItemSnapshot struct { Available decimal.Decimal USDClosePrice decimal.Decimal USDValue decimal.Decimal - Breakdown []Thing + Breakdown []CurrencyContribution } -type Thing struct { +// TODO look into this +type CurrencyContribution struct { Currency currency.Code USD decimal.Decimal } diff --git a/backtester/funding/pair.go b/backtester/funding/pair.go index 913d16a766a..e9d3e47bd91 100644 --- a/backtester/funding/pair.go +++ b/backtester/funding/pair.go @@ -16,25 +16,25 @@ var ( // BaseInitialFunds returns the initial funds // from the base in a currency pair func (p *Pair) BaseInitialFunds() decimal.Decimal { - return p.Base.initialFunds + return p.base.initialFunds } // QuoteInitialFunds returns the initial funds // from the quote in a currency pair func (p *Pair) QuoteInitialFunds() decimal.Decimal { - return p.Quote.initialFunds + return p.quote.initialFunds } // BaseAvailable returns the available funds // from the base in a currency pair func (p *Pair) BaseAvailable() decimal.Decimal { - return p.Base.available + return p.base.available } // QuoteAvailable returns the available funds // from the quote in a currency pair func (p *Pair) QuoteAvailable() decimal.Decimal { - return p.Quote.available + return p.quote.available } func (p *Pair) GetPairReader() (IPairReader, error) { @@ -51,15 +51,15 @@ func (p *Pair) GetCollateralReader() (ICollateralReader, error) { func (p *Pair) Reserve(amount decimal.Decimal, side order.Side) error { switch side { case order.Buy: - return p.Quote.Reserve(amount) + return p.quote.Reserve(amount) case order.Sell: - return p.Base.Reserve(amount) + return p.base.Reserve(amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", errCannotAllocate, - p.Base.exchange, - p.Base.asset, - p.Base.currency, + p.base.exchange, + p.base.asset, + p.base.currency, side) } } @@ -70,15 +70,15 @@ func (p *Pair) Reserve(amount decimal.Decimal, side order.Side) error { func (p *Pair) Release(amount, diff decimal.Decimal, side order.Side) error { switch side { case order.Buy: - return p.Quote.Release(amount, diff) + return p.quote.Release(amount, diff) case order.Sell: - return p.Base.Release(amount, diff) + return p.base.Release(amount, diff) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", errCannotAllocate, - p.Base.exchange, - p.Base.asset, - p.Base.currency, + p.base.exchange, + p.base.asset, + p.base.currency, side) } } @@ -88,9 +88,9 @@ func (p *Pair) Release(amount, diff decimal.Decimal, side order.Side) error { func (p *Pair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { switch side { case order.Buy: - p.Base.IncreaseAvailable(amount) + p.base.IncreaseAvailable(amount) case order.Sell: - p.Quote.IncreaseAvailable(amount) + p.quote.IncreaseAvailable(amount) } } @@ -100,9 +100,9 @@ func (p *Pair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { func (p *Pair) CanPlaceOrder(side order.Side) bool { switch side { case order.Buy: - return p.Quote.CanPlaceOrder() + return p.quote.CanPlaceOrder() case order.Sell: - return p.Base.CanPlaceOrder() + return p.base.CanPlaceOrder() } return false } @@ -138,8 +138,8 @@ func (p *Pair) FundReleaser() IFundReleaser { // Liquidate basic liquidation response to remove // all asset value func (p *Pair) Liquidate() { - p.Base.available = decimal.Zero - p.Base.reserved = decimal.Zero - p.Quote.available = decimal.Zero - p.Quote.reserved = decimal.Zero + p.base.available = decimal.Zero + p.base.reserved = decimal.Zero + p.quote.available = decimal.Zero + p.quote.reserved = decimal.Zero } From 52a58bdf2371603570754191bd295e4d6f9d8890 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 12 Apr 2022 09:30:17 +1000 Subject: [PATCH 118/171] Testwork --- backtester/engine/backtest_test.go | 54 +++++++-- backtester/eventhandlers/exchange/exchange.go | 1 - .../eventhandlers/portfolio/portfolio.go | 9 ++ .../eventhandlers/portfolio/portfolio_test.go | 80 +++++++++++++ .../ftxcashandcarry/ftxcashandcarry.go | 4 +- .../ftxcashandcarry/ftxcashandcarry_test.go | 113 ++++++++---------- 6 files changed, 183 insertions(+), 78 deletions(-) diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 250f79764c5..54403a9283f 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -2,7 +2,6 @@ package engine import ( "errors" - "os" "strings" "testing" "time" @@ -21,6 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/dollarcostaverage" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" evkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/funding" @@ -39,13 +39,7 @@ import ( const testExchange = "ftx" -var leet *decimal.Decimal - -func TestMain(m *testing.M) { - oneThreeThreeSeven := decimal.NewFromInt(1337) - leet = &oneThreeThreeSeven - os.Exit(m.Run()) -} +var leet = decimal.NewFromInt(1337) func TestNewFromConfig(t *testing.T) { t.Parallel() @@ -135,7 +129,7 @@ func TestLoadDataAPI(t *testing.T) { Base: cp.Base.String(), Quote: cp.Quote.String(), SpotDetails: &config.SpotDetails{ - InitialQuoteFunds: leet, + InitialQuoteFunds: &leet, }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, @@ -192,7 +186,7 @@ func TestLoadDataDatabase(t *testing.T) { Base: cp.Base.String(), Quote: cp.Quote.String(), SpotDetails: &config.SpotDetails{ - InitialQuoteFunds: leet, + InitialQuoteFunds: &leet, }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, @@ -260,7 +254,7 @@ func TestLoadDataCSV(t *testing.T) { Base: cp.Base.String(), Quote: cp.Quote.String(), SpotDetails: &config.SpotDetails{ - InitialQuoteFunds: leet, + InitialQuoteFunds: &leet, }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, @@ -318,7 +312,7 @@ func TestLoadDataLive(t *testing.T) { Base: cp.Base.String(), Quote: cp.Quote.String(), SpotDetails: &config.SpotDetails{ - InitialQuoteFunds: leet, + InitialQuoteFunds: &leet, }, BuySide: config.MinMax{}, SellSide: config.MinMax{}, @@ -686,8 +680,14 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, expectedError) } + cp := currency.NewPair(currency.BTC, currency.USDT) + a := asset.Futures expectedError = common.ErrNilArguments - ev := &evkline.Kline{} + ev := &evkline.Kline{ + Base: event.Base{Exchange: testExchange, + AssetType: a, + CurrencyPair: cp}, + } err = bt.triggerLiquidationsForExchange(ev, nil) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) @@ -695,6 +695,34 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { bt.Portfolio = &portfolioOverride{} pnl := &portfolio.PNLSummary{} + bt.Datas = &data.HandlerPerCurrency{} + d := data.Base{} + d.SetStream([]common.DataEventHandler{&evkline.Kline{ + Base: event.Base{ + Exchange: "ftx", + Time: time.Now(), + Interval: gctkline.OneDay, + CurrencyPair: cp, + AssetType: a, + }, + Open: decimal.NewFromInt(1337), + Close: decimal.NewFromInt(1337), + Low: decimal.NewFromInt(1337), + High: decimal.NewFromInt(1337), + Volume: decimal.NewFromInt(1337), + }}) + d.Next() + da := &kline.DataFromKline{ + Item: gctkline.Item{ + Exchange: testExchange, + Asset: a, + Pair: cp, + }, + Base: d, + RangeHolder: &gctkline.IntervalRangeHolder{}, + } + bt.Datas.SetDataForCurrency("ftx", a, cp, da) + //data.HandlerPerCurrency{} err = bt.triggerLiquidationsForExchange(ev, pnl) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 7ff2666f8e6..62406d7e394 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -308,7 +308,6 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c f.SetDirection(direction) e := fmt.Sprintf("Order size %v %s %s size %v", limitReducedAmount, belowExceed, minOrMax, size) f.AppendReason(e) - return fmt.Errorf("%w %v", errExceededPortfolioLimit, e) } return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 9aa124daae6..e89a23646a2 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -614,6 +614,15 @@ func (p *Portfolio) GetLatestPNLForEvent(e common.EventHandler) (*PNLSummary, er // CheckLiquidationStatus checks funding against position // and liquidates and removes funding if position unable to continue func (p *Portfolio) CheckLiquidationStatus(ev common.DataEventHandler, collateralReader funding.ICollateralReader, pnl *PNLSummary) error { + if ev == nil { + return common.ErrNilEvent + } + if collateralReader == nil { + return fmt.Errorf("%w collateral reader missing", common.ErrNilArguments) + } + if pnl == nil { + return fmt.Errorf("%w pnl summary missing", common.ErrNilArguments) + } availableFunds := collateralReader.AvailableFunds() position, err := p.GetLatestPosition(ev) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 766bd3ee4f4..ab46b9c77fe 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -1398,3 +1398,83 @@ func TestGetPositionStatus(t *testing.T) { t.Errorf("expected '%v' received '%v'", gctorder.Rejected, status) } } + +func TestCheckLiquidationStatus(t *testing.T) { + t.Parallel() + p := &Portfolio{} + var expectedError = common.ErrNilEvent + err := p.CheckLiquidationStatus(nil, nil, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + + ev := &kline.Kline{} + expectedError = common.ErrNilArguments + err = p.CheckLiquidationStatus(ev, nil, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + + item := asset.Futures + pair := currency.NewPair(currency.BTC, currency.USD) + expectedError = nil + contract, err := funding.CreateItem(testExchange, item, pair.Base, decimal.NewFromInt(100), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + collateral, err := funding.CreateItem(testExchange, item, pair.Quote, decimal.NewFromInt(100), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + collat, err := funding.CreateCollateral(contract, collateral) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + expectedError = common.ErrNilArguments + err = p.CheckLiquidationStatus(ev, collat, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + + pnl := &PNLSummary{} + expectedError = gctorder.ErrNotFuturesAsset + err = p.CheckLiquidationStatus(ev, collat, pnl) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + + pnl.Item = asset.Futures + ev.AssetType = asset.Futures + ev.Exchange = "ftx" + ev.CurrencyPair = pair + exch := &ftx.FTX{} + exch.Name = testExchange + expectedError = nil + err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: exch, Asset: asset.Futures, Pair: pair}) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + settings, err := p.getSettings(testExchange, ev.AssetType, ev.Pair()) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + od := &gctorder.Detail{ + Price: 1336, + Amount: 20, + Exchange: exch.Name, + Side: gctorder.Short, + AssetType: ev.AssetType, + Date: time.Now(), + Pair: pair, + ID: "lol", + } + err = settings.FuturesTracker.TrackNewOrder(od) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } + err = p.CheckLiquidationStatus(ev, collat, pnl) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v', expected '%v'", err, expectedError) + } +} diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 67b21f3fdce..42118556d85 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -53,7 +53,7 @@ var errNotSetup = errors.New("sent incomplete signals") // in allowing a strategy to only place an order for X currency if Y currency's price is Z func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTransferer, p portfolio.Handler) ([]signal.Event, error) { var response []signal.Event - sortedSignals, err := sortSignals(d, f) + sortedSignals, err := sortSignals(d) if err != nil { return nil, err } @@ -141,7 +141,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra return response, nil } -func sortSignals(d []data.Handler, f funding.IFundingTransferer) (map[currency.Pair]cashCarrySignals, error) { +func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { var response = make(map[currency.Pair]cashCarrySignals) for i := range d { l := d[i].Latest() diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index ec501a17022..8f54ca5cb8e 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -26,6 +26,14 @@ func TestName(t *testing.T) { } } +func TestDescription(t *testing.T) { + t.Parallel() + d := Strategy{} + if n := d.Description(); n != description { + t.Errorf("expected %v", description) + } +} + func TestSupportsSimultaneousProcessing(t *testing.T) { t.Parallel() s := Strategy{} @@ -81,16 +89,18 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, base.ErrSimultaneousProcessingOnly) { t.Errorf("received: %v, expected: %v", err, base.ErrSimultaneousProcessingOnly) } - dStart := time.Date(2020, 1, 0, 0, 0, 0, 0, time.UTC) +} + +func TestOnSignals(t *testing.T) { + t.Parallel() + s := Strategy{} dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - dEnd := time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) exch := "ftx" - a := asset.Futures + a := asset.Spot p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ Base: event.Base{ - Offset: 3, Exchange: exch, Time: dInsert, Interval: gctkline.OneDay, @@ -102,64 +112,34 @@ func TestOnSignal(t *testing.T) { Low: decimal.NewFromInt(1337), High: decimal.NewFromInt(1337), Volume: decimal.NewFromInt(1337), - }}, - ) + }}) d.Next() da := &kline.DataFromKline{ Item: gctkline.Item{}, Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, } - _, err = s.OnSignal(da, nil, nil) - if !errors.Is(err, base.ErrSimultaneousProcessingOnly) { - t.Fatalf("expected: %v, received %v", nil, base.ErrSimultaneousProcessingOnly) - } - s.openShortDistancePercentage = decimal.NewFromInt(1) - _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) - if !errors.Is(err, errNotSetup) { - t.Fatalf("expected: %v, received %v", nil, errNotSetup) - } - - da.Item = gctkline.Item{ - Exchange: exch, - Pair: p, - Asset: a, - Interval: gctkline.OneDay, - Candles: []gctkline.Candle{ - { - Time: dInsert, - Open: 1337, - High: 1337, - Low: 1337, - Close: 1337, - Volume: 1337, - }, - }, - } - err = da.Load() - if err != nil { - t.Error(err) + _, err := s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) + if !strings.Contains(err.Error(), errNotSetup.Error()) { + // common.Errs type doesn't keep type + t.Errorf("received: %v, expected: %v", err, errNotSetup) } +} - ranger, err := gctkline.CalculateCandleDateRanges(dStart, dEnd, gctkline.OneDay, 100000) - if err != nil { - t.Error(err) +func TestSetDefaults(t *testing.T) { + t.Parallel() + s := Strategy{} + s.SetDefaults() + if !s.openShortDistancePercentage.Equal(decimal.NewFromInt(0)) { + t.Errorf("expected 5, received %v", s.openShortDistancePercentage) } - da.RangeHolder = ranger - da.RangeHolder.SetHasDataFromCandles(da.Item.Candles) - _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) - if !errors.Is(err, errNotSetup) { - t.Fatalf("expected: %v, received %v", nil, errNotSetup) + if !s.closeShortDistancePercentage.Equal(decimal.NewFromInt(0)) { + t.Errorf("expected 5, received %v", s.closeShortDistancePercentage) } } -func TestOnSignals(t *testing.T) { +func TestSortSignals(t *testing.T) { t.Parallel() - s := Strategy{} - _, err := s.OnSignal(nil, nil, nil) - if !errors.Is(err, base.ErrSimultaneousProcessingOnly) { - t.Errorf("received: %v, expected: %v", err, base.ErrSimultaneousProcessingOnly) - } dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) exch := "ftx" a := asset.Spot @@ -185,21 +165,30 @@ func TestOnSignals(t *testing.T) { Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, } - _, err = s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) - if !strings.Contains(err.Error(), errNotSetup.Error()) { - // common.Errs type doesn't keep type + _, err := sortSignals([]data.Handler{da}) + if !errors.Is(err, errNotSetup) { t.Errorf("received: %v, expected: %v", err, errNotSetup) } -} -func TestSetDefaults(t *testing.T) { - t.Parallel() - s := Strategy{} - s.SetDefaults() - if !s.openShortDistancePercentage.Equal(decimal.NewFromInt(0)) { - t.Errorf("expected 5, received %v", s.openShortDistancePercentage) - } - if !s.closeShortDistancePercentage.Equal(decimal.NewFromInt(0)) { - t.Errorf("expected 5, received %v", s.closeShortDistancePercentage) + d2 := data.Base{} + d2.SetStream([]common.DataEventHandler{&eventkline.Kline{ + Base: event.Base{ + Exchange: exch, + Time: dInsert, + Interval: gctkline.OneDay, + CurrencyPair: p, + AssetType: asset.Futures, + }, + Open: decimal.NewFromInt(1337), + Close: decimal.NewFromInt(1337), + Low: decimal.NewFromInt(1337), + High: decimal.NewFromInt(1337), + Volume: decimal.NewFromInt(1337), + }}) + d.Next() + da := &kline.DataFromKline{ + Item: gctkline.Item{}, + Base: d, + RangeHolder: &gctkline.IntervalRangeHolder{}, } } From 26e0f5609e32277f955c79177a25fb47f29618c6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 12 Apr 2022 16:28:15 +1000 Subject: [PATCH 119/171] boring tests --- .../ftxcashandcarry/ftxcashandcarry_test.go | 2 +- backtester/eventtypes/event/event_test.go | 40 +++++++++ backtester/eventtypes/fill/fill_test.go | 38 ++++++++ backtester/eventtypes/kline/kline_test.go | 14 +++ backtester/eventtypes/order/order_test.go | 22 +++++ backtester/eventtypes/signal/signal_test.go | 86 +++++++++++++++++++ backtester/eventtypes/signal/signal_types.go | 2 +- 7 files changed, 202 insertions(+), 2 deletions(-) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 8f54ca5cb8e..8bb8fc4ae06 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -186,7 +186,7 @@ func TestSortSignals(t *testing.T) { Volume: decimal.NewFromInt(1337), }}) d.Next() - da := &kline.DataFromKline{ + da = &kline.DataFromKline{ Item: gctkline.Item{}, Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, diff --git a/backtester/eventtypes/event/event_test.go b/backtester/eventtypes/event/event_test.go index 70fdf984711..794cd5aa2c3 100644 --- a/backtester/eventtypes/event/event_test.go +++ b/backtester/eventtypes/event/event_test.go @@ -18,6 +18,12 @@ func TestEvent_AppendWhy(t *testing.T) { if !strings.Contains(y, "test") { t.Error("expected test") } + e.AppendReason("test") + y = e.GetReason() + if y != "test. test" { + t.Error("expected 'test. test'") + } + } func TestEvent_GetAssetType(t *testing.T) { @@ -81,3 +87,37 @@ func TestEvent_Pair(t *testing.T) { t.Error("expected currency") } } + +func TestGetOffset(t *testing.T) { + t.Parallel() + b := Base{ + Offset: 1337, + } + if b.GetOffset() != 1337 { + t.Error("expected 1337") + } +} + +func TestSetOffset(t *testing.T) { + t.Parallel() + b := Base{ + Offset: 1337, + } + b.SetOffset(1339) + if b.Offset != 1339 { + t.Error("expected 1339") + } +} + +func TestAppendReasonf(t *testing.T) { + t.Parallel() + b := Base{} + b.AppendReasonf("%v", "hello moto") + if b.Reason != "hello moto" { + t.Errorf("epected hello moto, received '%v'", b.Reason) + } + b.AppendReasonf("%v %v", "hello", "moto") + if b.Reason != "hello moto. hello moto" { + t.Errorf("epected 'hello moto. hello moto', received '%v'", b.Reason) + } +} diff --git a/backtester/eventtypes/fill/fill_test.go b/backtester/eventtypes/fill/fill_test.go index 1d343d9ba6d..53788874f0f 100644 --- a/backtester/eventtypes/fill/fill_test.go +++ b/backtester/eventtypes/fill/fill_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -89,3 +90,40 @@ func TestGetSlippageRate(t *testing.T) { t.Error("expected 1") } } + +func TestGetTotal(t *testing.T) { + t.Parallel() + f := Fill{} + f.Total = decimal.NewFromInt(1337) + e := f.GetTotal() + if !e.Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") + } +} + +func TestGetFillDependentEvent(t *testing.T) { + t.Parallel() + f := Fill{} + if f.GetFillDependentEvent() != nil { + t.Error("expected nil") + } + f.FillDependentEvent = &signal.Signal{ + Amount: decimal.NewFromInt(1337), + } + e := f.GetFillDependentEvent() + if !e.GetAmount().Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") + } +} + +func TestIsLiquidated(t *testing.T) { + t.Parallel() + f := Fill{} + if f.IsLiquidated() { + t.Error("expected false") + } + f.Liquidated = true + if !f.IsLiquidated() { + t.Error("expected true") + } +} diff --git a/backtester/eventtypes/kline/kline_test.go b/backtester/eventtypes/kline/kline_test.go index 663c0cc806b..d2f7d2deca7 100644 --- a/backtester/eventtypes/kline/kline_test.go +++ b/backtester/eventtypes/kline/kline_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/currency" ) func TestClose(t *testing.T) { @@ -45,3 +47,15 @@ func TestOpen(t *testing.T) { t.Error("expected decimal.NewFromInt(1337)") } } + +func TestGetUnderlyingPair(t *testing.T) { + t.Parallel() + k := Kline{ + Base: event.Base{ + UnderlyingPair: currency.NewPair(currency.USD, currency.DOGE), + }, + } + if !k.GetUnderlyingPair().Equal(k.Base.UnderlyingPair) { + t.Errorf("expected '%v'", k.Base.UnderlyingPair) + } +} diff --git a/backtester/eventtypes/order/order_test.go b/backtester/eventtypes/order/order_test.go index 447e51dc511..c538cf93cf0 100644 --- a/backtester/eventtypes/order/order_test.go +++ b/backtester/eventtypes/order/order_test.go @@ -84,3 +84,25 @@ func TestGetFunds(t *testing.T) { t.Error("expected decimal.NewFromInt(1337)") } } + +func TestOpen(t *testing.T) { + t.Parallel() + k := Order{ + ClosePrice: decimal.NewFromInt(1337), + } + if !k.GetClosePrice().Equal(decimal.NewFromInt(1337)) { + t.Error("expected decimal.NewFromInt(1337)") + } +} + +func TestIsLiquidating(t *testing.T) { + t.Parallel() + k := Order{} + if k.IsLiquidating() { + t.Error("expected false") + } + k.LiquidatingPosition = true + if !k.IsLiquidating() { + t.Error("expected true") + } +} diff --git a/backtester/eventtypes/signal/signal_test.go b/backtester/eventtypes/signal/signal_test.go index 98c77769a33..5692fa0f9dc 100644 --- a/backtester/eventtypes/signal/signal_test.go +++ b/backtester/eventtypes/signal/signal_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/currency" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -56,3 +58,87 @@ func TestSetSellLimit(t *testing.T) { t.Errorf("expected 20, received %v", s.GetSellLimit()) } } + +func TestGetAmount(t *testing.T) { + t.Parallel() + s := Signal{ + Amount: decimal.NewFromInt(1337), + } + if !s.GetAmount().Equal(decimal.NewFromInt(1337)) { + t.Error("expected decimal.NewFromInt(1337)") + } +} + +func TestSetAmount(t *testing.T) { + t.Parallel() + s := Signal{} + s.SetAmount(decimal.NewFromInt(1337)) + if !s.GetAmount().Equal(decimal.NewFromInt(1337)) { + t.Error("expected decimal.NewFromInt(1337)") + } +} + +func TestGetUnderlyingPair(t *testing.T) { + t.Parallel() + s := Signal{ + Base: event.Base{ + UnderlyingPair: currency.NewPair(currency.USD, currency.DOGE), + }, + } + if !s.GetUnderlyingPair().Equal(s.Base.UnderlyingPair) { + t.Errorf("expected '%v'", s.Base.UnderlyingPair) + } +} + +func TestPair(t *testing.T) { + t.Parallel() + s := Signal{ + Base: event.Base{ + CurrencyPair: currency.NewPair(currency.USD, currency.DOGE), + }, + } + if !s.Pair().Equal(s.Base.UnderlyingPair) { + t.Errorf("expected '%v'", s.Base.CurrencyPair) + } +} + +func TestGetFillDependentEvent(t *testing.T) { + t.Parallel() + s := Signal{} + if a := s.GetFillDependentEvent(); a != nil { + t.Error("expected nil") + } + s.FillDependentEvent = &Signal{ + Amount: decimal.NewFromInt(1337), + } + e := s.GetFillDependentEvent() + if !e.GetAmount().Equal(decimal.NewFromInt(1337)) { + t.Error("expected 1337") + } +} + +func TestGetCollateralCurrency(t *testing.T) { + t.Parallel() + s := Signal{} + c := s.GetCollateralCurrency() + if !c.IsEmpty() { + t.Error("expected empty currency") + } + s.CollateralCurrency = currency.BTC + c = s.GetCollateralCurrency() + if !c.Equal(currency.BTC) { + t.Error("expected empty currency") + } +} + +func TestIsNil(t *testing.T) { + t.Parallel() + s := &Signal{} + if s.IsNil() { + t.Error("expected false") + } + s = nil + if !s.IsNil() { + t.Error("expected true") + } +} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index c23f6a8545f..4feea2de952 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -49,7 +49,7 @@ type Signal struct { // FillDependentEvent ensures that an order can only be placed // if there is corresponding collateral in the selected currency // this enabled cash and carry strategies for example - FillDependentEvent *Signal + FillDependentEvent Event // CollateralCurrency is an optional paramater // when using futures to limit the collateral available // to a singular currency From e1fbbe7d76f2568fda57ebb252880c84824be2ee Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 14 Apr 2022 16:12:56 +1000 Subject: [PATCH 120/171] engine coverage --- backtester/data/data.go | 9 +- backtester/data/data_test.go | 5 +- backtester/data/data_types.go | 6 +- backtester/engine/backtest.go | 28 ++- backtester/engine/backtest_test.go | 195 +++++++++++++++++- backtester/eventhandlers/exchange/exchange.go | 1 + backtester/eventtypes/signal/signal_test.go | 2 +- engine/order_manager_test.go | 2 +- 8 files changed, 231 insertions(+), 17 deletions(-) diff --git a/backtester/data/data.go b/backtester/data/data.go index d5e05bd676d..6a28c7a31a3 100644 --- a/backtester/data/data.go +++ b/backtester/data/data.go @@ -1,6 +1,7 @@ package data import ( + "fmt" "sort" "strings" @@ -37,8 +38,12 @@ func (h *HandlerPerCurrency) GetAllData() map[string]map[asset.Item]map[currency } // GetDataForCurrency returns the Handler for a specific exchange, asset, currency -func (h *HandlerPerCurrency) GetDataForCurrency(ev common.EventHandler) Handler { - return h.data[ev.GetExchange()][ev.GetAssetType()][ev.Pair()] +func (h *HandlerPerCurrency) GetDataForCurrency(ev common.EventHandler) (Handler, error) { + handler, ok := h.data[ev.GetExchange()][ev.GetAssetType()][ev.Pair()] + if !ok { + return nil, fmt.Errorf("%s %s %s %w", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), ErrHandlerNotFound) + } + return handler, nil } // Reset returns the struct to defaults diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index dc1e6e4ff82..39bef31e423 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -176,7 +176,10 @@ func TestGetDataForCurrency(t *testing.T) { AssetType: a, CurrencyPair: p, }} - result := d.GetDataForCurrency(ev) + result, err := d.GetDataForCurrency(ev) + if err != nil { + t.Error(err) + } if result != nil { t.Error("expected nil") } diff --git a/backtester/data/data_types.go b/backtester/data/data_types.go index b62244b7d3f..2edc681303e 100644 --- a/backtester/data/data_types.go +++ b/backtester/data/data_types.go @@ -1,6 +1,7 @@ package data import ( + "errors" "time" "github.com/shopspring/decimal" @@ -9,6 +10,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) +// ErrHandlerNotFound returned when a handler is not found for specified exchange, asset, pair +var ErrHandlerNotFound = errors.New("handler not found") + // HandlerPerCurrency stores an event handler per exchange asset pair type HandlerPerCurrency struct { data map[string]map[asset.Item]map[currency.Pair]Handler @@ -19,7 +23,7 @@ type Holder interface { Setup() SetDataForCurrency(string, asset.Item, currency.Pair, Handler) GetAllData() map[string]map[asset.Item]map[currency.Pair]Handler - GetDataForCurrency(ev common.EventHandler) Handler + GetDataForCurrency(ev common.EventHandler) (Handler, error) Reset() } diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index a6772d20dd5..e5ff59990d4 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -141,7 +141,10 @@ func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds fun if err != nil { return err } - d := bt.Datas.GetDataForCurrency(ev) + d, err := bt.Datas.GetDataForCurrency(ev) + if err != nil { + return err + } s, err := bt.Strategy.OnSignal(d, bt.Funding, bt.Portfolio) if err != nil { if errors.Is(err, base.ErrTooMuchBadData) { @@ -216,18 +219,24 @@ func (bt *BackTest) processSimultaneousDataEvents() error { // updateStatsForDataEvent makes various systems aware of price movements from // data events func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds funding.IFundReleaser) error { + if ev == nil { + return common.ErrNilEvent + } + if funds == nil { + return fmt.Errorf("%v %v %v %w missing fund releaser", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), common.ErrNilArguments) + } // update statistics with the latest price err := bt.Statistic.SetupEventForTime(ev) if err != nil { if errors.Is(err, statistics.ErrAlreadyProcessed) { return err } - log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v", err) } // update portfolio manager with the latest price err = bt.Portfolio.UpdateHoldings(ev, funds) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "UpdateHoldings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "UpdateHoldings %v", err) } if ev.GetAssetType().IsFutures() { @@ -239,7 +248,7 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice()) if err != nil && !errors.Is(err, gctorder.ErrPositionLiquidated) { - return fmt.Errorf("UpdatePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return fmt.Errorf("UpdatePNL %v", err) } var pnl *portfolio.PNLSummary pnl, err = bt.Portfolio.GetLatestPNLForEvent(ev) @@ -283,7 +292,11 @@ func (bt *BackTest) triggerLiquidationsForExchange(ev common.DataEventHandler, p // which may not have been processed yet // this will create and store stats for each order // then liquidate it at the funding level - datas := bt.Datas.GetDataForCurrency(orders[i]) + var datas data.Handler + datas, err = bt.Datas.GetDataForCurrency(orders[i]) + if err != nil { + return err + } latest := datas.Latest() err = bt.Statistic.SetupEventForTime(latest) if err != nil && !errors.Is(err, statistics.ErrAlreadyProcessed) { @@ -328,7 +341,10 @@ func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReser } func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundReleaser) error { - d := bt.Datas.GetDataForCurrency(ev) + d, err := bt.Datas.GetDataForCurrency(ev) + if err != nil { + return err + } f, err := bt.Exchange.ExecuteOrder(ev, d, bt.orderManager, funds) if err != nil { if f == nil { diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 54403a9283f..5c3b7004b12 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -21,8 +21,10 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/dollarcostaverage" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" evkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/backtester/report" gctcommon "github.com/thrasher-corp/gocryptotrader/common" @@ -35,6 +37,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) const testExchange = "ftx" @@ -662,12 +665,25 @@ type portfolioOverride struct { portfolio.Portfolio } -func (p portfolioOverride) CreateLiquidationOrdersForExchange(common.DataEventHandler, funding.IFundingManager) ([]order.Event, error) { +func (p portfolioOverride) CreateLiquidationOrdersForExchange(ev common.DataEventHandler, _ funding.IFundingManager) ([]order.Event, error) { if p.Err != nil { return nil, p.Err } return []order.Event{ - &order.Order{}, + &order.Order{ + Base: event.Base{ + Offset: ev.GetOffset(), + Exchange: ev.GetExchange(), + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: ev.Pair(), + UnderlyingPair: ev.GetUnderlyingPair(), + AssetType: ev.GetAssetType(), + Reason: ev.GetReason(), + }, + ID: "1", + Direction: common.Liquidated, + }, }, nil } @@ -699,7 +715,7 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { d := data.Base{} d.SetStream([]common.DataEventHandler{&evkline.Kline{ Base: event.Base{ - Exchange: "ftx", + Exchange: testExchange, Time: time.Now(), Interval: gctkline.OneDay, CurrencyPair: cp, @@ -721,10 +737,179 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, } - bt.Datas.SetDataForCurrency("ftx", a, cp, da) - //data.HandlerPerCurrency{} + bt.Statistic = &statistics.Statistic{} + expectedError = nil + + bt.EventQueue = &eventholder.Holder{} + bt.Funding = &funding.FundManager{} + bt.Datas.SetDataForCurrency(testExchange, a, cp, da) + err = bt.Statistic.SetupEventForTime(ev) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + pnl.Exchange = ev.Exchange + pnl.Item = ev.AssetType + pnl.Pair = ev.CurrencyPair err = bt.triggerLiquidationsForExchange(ev, pnl) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } + ev2 := bt.EventQueue.NextEvent() + ev2o, ok := ev2.(order.Event) + if !ok { + t.Fatal("expected order event") + } + if ev2o.GetDirection() != common.Liquidated { + t.Error("expected liquidation order") + } +} + +func TestUpdateStatsForDataEvent(t *testing.T) { + pt := &portfolio.Portfolio{} + bt := &BackTest{ + Statistic: &statistics.Statistic{}, + Funding: &funding.FundManager{}, + Portfolio: pt, + } + expectedError := common.ErrNilEvent + err := bt.updateStatsForDataEvent(nil, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + cp := currency.NewPair(currency.BTC, currency.USDT) + a := asset.Futures + ev := &evkline.Kline{ + Base: event.Base{Exchange: testExchange, + AssetType: a, + CurrencyPair: cp}, + } + + expectedError = common.ErrNilArguments + err = bt.updateStatsForDataEvent(ev, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + expectedError = nil + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + b, err := funding.CreateItem(testExchange, a, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + quote, err := funding.CreateItem(testExchange, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + pair, err := funding.CreateCollateral(b, quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt.Funding = f + exch := &ftx.FTX{} + exch.Name = testExchange + err = pt.SetupCurrencySettingsMap(&exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + ev.Time = time.Now() + fl := &fill.Fill{ + Base: ev.Base, + Direction: gctorder.Short, + Amount: decimal.NewFromInt(1), + ClosePrice: decimal.NewFromInt(1), + VolumeAdjustedPrice: decimal.NewFromInt(1), + PurchasePrice: decimal.NewFromInt(1), + Total: decimal.NewFromInt(1), + Slippage: decimal.NewFromInt(1), + Order: &gctorder.Detail{ + Exchange: testExchange, + AssetType: ev.AssetType, + Pair: cp, + Amount: 1, + Price: 1, + Side: gctorder.Short, + ID: "1", + Date: time.Now(), + }, + } + _, err = pt.TrackFuturesOrder(fl, pair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + err = bt.updateStatsForDataEvent(ev, pair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } +} + +func TestProcessSignalEvent(t *testing.T) { + var expectedError error + pt, err := portfolio.Setup(&size.Size{}, &risk.Risk{}, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt := &BackTest{ + Statistic: &statistics.Statistic{}, + Funding: &funding.FundManager{}, + Portfolio: pt, + Exchange: &exchange.Exchange{}, + } + cp := currency.NewPair(currency.BTC, currency.USDT) + a := asset.Futures + ev := &signal.Signal{ + Base: event.Base{Exchange: testExchange, + AssetType: a, + CurrencyPair: cp}, + } + + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + b, err := funding.CreateItem(testExchange, a, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + quote, err := funding.CreateItem(testExchange, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + pair, err := funding.CreateCollateral(b, quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt.Funding = f + exch := &ftx.FTX{} + exch.Name = testExchange + err = pt.SetupCurrencySettingsMap(&exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + bt.Exchange.SetExchangeAssetCurrencySettings(a, cp, &exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + ev.Direction = gctorder.Short + err = bt.Statistic.SetEventForOffset(ev) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = bt.processSignalEvent(ev, pair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 62406d7e394..44d2b8d49f2 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -308,6 +308,7 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c f.SetDirection(direction) e := fmt.Sprintf("Order size %v %s %s size %v", limitReducedAmount, belowExceed, minOrMax, size) f.AppendReason(e) + return errExceededPortfolioLimit } return nil } diff --git a/backtester/eventtypes/signal/signal_test.go b/backtester/eventtypes/signal/signal_test.go index 5692fa0f9dc..61e547a6c5f 100644 --- a/backtester/eventtypes/signal/signal_test.go +++ b/backtester/eventtypes/signal/signal_test.go @@ -97,7 +97,7 @@ func TestPair(t *testing.T) { CurrencyPair: currency.NewPair(currency.USD, currency.DOGE), }, } - if !s.Pair().Equal(s.Base.UnderlyingPair) { + if !s.Pair().Equal(s.Base.CurrencyPair) { t.Errorf("expected '%v'", s.Base.CurrencyPair) } } diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index ddf4172ddb1..ccccd954571 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -769,7 +769,7 @@ func TestProcessOrders(t *testing.T) { IBotExchange: exch, } em.Add(fakeExchange) - m, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false) + m, err := SetupOrderManager(em, &CommunicationManager{}, &wg, false, false) if !errors.Is(err, nil) { t.Errorf("error '%v', expected '%v'", err, nil) } From 36295ddbe7cbdb9bc52ece07a7b37fd3e27b0592 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 15 Apr 2022 10:27:24 +1000 Subject: [PATCH 121/171] More backtesting coverage --- backtester/engine/backtest.go | 74 ++- backtester/engine/backtest_test.go | 514 ++++++++++++++++-- backtester/eventhandlers/exchange/exchange.go | 15 +- 3 files changed, 537 insertions(+), 66 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index e5ff59990d4..8552a9f933a 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -320,6 +320,12 @@ func (bt *BackTest) triggerLiquidationsForExchange(ev common.DataEventHandler, p // processSignalEvent receives an event from the strategy for processing under the portfolio func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReserver) error { + if ev == nil { + return common.ErrNilEvent + } + if funds == nil { + return fmt.Errorf("%w funds", common.ErrNilArguments) + } cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) @@ -341,6 +347,12 @@ func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReser } func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundReleaser) error { + if ev == nil { + return common.ErrNilEvent + } + if funds == nil { + return fmt.Errorf("%w funds", common.ErrNilArguments) + } d, err := bt.Datas.GetDataForCurrency(ev) if err != nil { return err @@ -411,42 +423,50 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) bt.EventQueue.AppendEvent(fde) } if ev.GetAssetType().IsFutures() { - if ev.GetOrder() != nil { - pnl, err := bt.Portfolio.TrackFuturesOrder(ev, funds) - if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { - return fmt.Errorf("TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - } + err = bt.processFuturesFillEvent(ev, funds) + if err != nil { + return err + } + } + return nil +} - var exch gctexchange.IBotExchange - exch, err = bt.exchangeManager.GetExchangeByName(ev.GetExchange()) - if err != nil { - return fmt.Errorf("GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - } +func (bt *BackTest) processFuturesFillEvent(ev fill.Event, funds funding.IFundReleaser) error { + if ev.GetOrder() != nil { + pnl, err := bt.Portfolio.TrackFuturesOrder(ev, funds) + if err != nil && !errors.Is(err, gctorder.ErrSubmissionIsNil) { + return fmt.Errorf("TrackFuturesOrder %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } - rPNL := pnl.GetRealisedPNL() - if !rPNL.PNL.IsZero() { - var receivingCurrency currency.Code - var receivingAsset asset.Item - receivingCurrency, receivingAsset, err = exch.GetCurrencyForRealisedPNL(ev.GetAssetType(), ev.Pair()) - if err != nil { - return fmt.Errorf("GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - } - err = bt.Funding.RealisePNL(ev.GetExchange(), receivingAsset, receivingCurrency, rPNL.PNL) - if err != nil { - return fmt.Errorf("RealisePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) - } - } + var exch gctexchange.IBotExchange + exch, err = bt.exchangeManager.GetExchangeByName(ev.GetExchange()) + if err != nil { + return fmt.Errorf("GetExchangeByName %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } - err = bt.Statistic.AddPNLForTime(pnl) + rPNL := pnl.GetRealisedPNL() + if !rPNL.PNL.IsZero() { + var receivingCurrency currency.Code + var receivingAsset asset.Item + receivingCurrency, receivingAsset, err = exch.GetCurrencyForRealisedPNL(ev.GetAssetType(), ev.Pair()) + if err != nil { + return fmt.Errorf("GetCurrencyForRealisedPNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } + err = bt.Funding.RealisePNL(ev.GetExchange(), receivingAsset, receivingCurrency, rPNL.PNL) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + return fmt.Errorf("RealisePNL %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } - err = bt.Funding.UpdateCollateral(ev) + + err = bt.Statistic.AddPNLForTime(pnl) if err != nil { - return fmt.Errorf("UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } + err := bt.Funding.UpdateCollateral(ev) + if err != nil { + return fmt.Errorf("UpdateCollateral %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + } return nil } diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 5c3b7004b12..d3bb94d84fd 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -44,6 +44,33 @@ const testExchange = "ftx" var leet = decimal.NewFromInt(1337) +type portfolioOverride struct { + Err error + portfolio.Portfolio +} + +func (p portfolioOverride) CreateLiquidationOrdersForExchange(ev common.DataEventHandler, _ funding.IFundingManager) ([]order.Event, error) { + if p.Err != nil { + return nil, p.Err + } + return []order.Event{ + &order.Order{ + Base: event.Base{ + Offset: ev.GetOffset(), + Exchange: ev.GetExchange(), + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: ev.Pair(), + UnderlyingPair: ev.GetUnderlyingPair(), + AssetType: ev.GetAssetType(), + Reason: ev.GetReason(), + }, + ID: "1", + Direction: common.Liquidated, + }, + }, nil +} + func TestNewFromConfig(t *testing.T) { t.Parallel() _, err := NewFromConfig(nil, "", "", false) @@ -660,33 +687,6 @@ func TestFullCycleMulti(t *testing.T) { } } -type portfolioOverride struct { - Err error - portfolio.Portfolio -} - -func (p portfolioOverride) CreateLiquidationOrdersForExchange(ev common.DataEventHandler, _ funding.IFundingManager) ([]order.Event, error) { - if p.Err != nil { - return nil, p.Err - } - return []order.Event{ - &order.Order{ - Base: event.Base{ - Offset: ev.GetOffset(), - Exchange: ev.GetExchange(), - Time: ev.GetTime(), - Interval: ev.GetInterval(), - CurrencyPair: ev.Pair(), - UnderlyingPair: ev.GetUnderlyingPair(), - AssetType: ev.GetAssetType(), - Reason: ev.GetReason(), - }, - ID: "1", - Direction: common.Liquidated, - }, - }, nil -} - func TestTriggerLiquidationsForExchange(t *testing.T) { t.Parallel() bt := BackTest{} @@ -765,6 +765,7 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { } func TestUpdateStatsForDataEvent(t *testing.T) { + t.Parallel() pt := &portfolio.Portfolio{} bt := &BackTest{ Statistic: &statistics.Statistic{}, @@ -851,24 +852,33 @@ func TestUpdateStatsForDataEvent(t *testing.T) { } func TestProcessSignalEvent(t *testing.T) { + t.Parallel() var expectedError error pt, err := portfolio.Setup(&size.Size{}, &risk.Risk{}, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } bt := &BackTest{ - Statistic: &statistics.Statistic{}, - Funding: &funding.FundManager{}, - Portfolio: pt, - Exchange: &exchange.Exchange{}, + Statistic: &statistics.Statistic{}, + Funding: &funding.FundManager{}, + Portfolio: pt, + Exchange: &exchange.Exchange{}, + EventQueue: &eventholder.Holder{}, } cp := currency.NewPair(currency.BTC, currency.USDT) a := asset.Futures - ev := &signal.Signal{ + de := &evkline.Kline{ Base: event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } + err = bt.Statistic.SetupEventForTime(de) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + ev := &signal.Signal{ + Base: de.Base, + } f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) if !errors.Is(err, expectedError) { @@ -913,3 +923,443 @@ func TestProcessSignalEvent(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, expectedError) } } + +func TestProcessOrderEvent(t *testing.T) { + t.Parallel() + var expectedError error + pt, err := portfolio.Setup(&size.Size{}, &risk.Risk{}, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt := &BackTest{ + Statistic: &statistics.Statistic{}, + Funding: &funding.FundManager{}, + Portfolio: pt, + Exchange: &exchange.Exchange{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + } + cp := currency.NewPair(currency.BTC, currency.USDT) + a := asset.Futures + de := &evkline.Kline{ + Base: event.Base{Exchange: testExchange, + AssetType: a, + CurrencyPair: cp}, + } + err = bt.Statistic.SetupEventForTime(de) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + ev := &order.Order{ + Base: de.Base, + } + + f, err := funding.SetupFundingManager(&engine.ExchangeManager{}, false, true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + b, err := funding.CreateItem(testExchange, a, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + quote, err := funding.CreateItem(testExchange, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + pair, err := funding.CreateCollateral(b, quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt.Funding = f + exch := &ftx.FTX{} + exch.Name = testExchange + err = pt.SetupCurrencySettingsMap(&exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + bt.Exchange.SetExchangeAssetCurrencySettings(a, cp, &exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + ev.Direction = gctorder.Short + err = bt.Statistic.SetEventForOffset(ev) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + tt := time.Now() + bt.Datas.Setup() + k := kline.DataFromKline{ + Item: gctkline.Item{ + Exchange: testExchange, + Pair: cp, + Asset: a, + Interval: gctkline.FifteenMin, + Candles: []gctkline.Candle{{ + Time: tt, + Open: 1337, + High: 1337, + Low: 1337, + Close: 1337, + Volume: 1337, + }}, + }, + Base: data.Base{}, + RangeHolder: &gctkline.IntervalRangeHolder{ + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + Ranges: []gctkline.IntervalRange{ + { + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + Intervals: []gctkline.IntervalData{ + { + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + HasData: true, + }, + }, + }, + }, + }, + } + err = k.Load() + if err != nil { + t.Error(err) + } + + bt.Datas.SetDataForCurrency(testExchange, a, cp, &k) + err = bt.processOrderEvent(ev, pair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + ev2 := bt.EventQueue.NextEvent() + _, ok := ev2.(fill.Event) + if !ok { + t.Fatal("expected fill event") + } +} + +func TestProcessFillEvent(t *testing.T) { + t.Parallel() + var expectedError error + pt, err := portfolio.Setup(&size.Size{}, &risk.Risk{}, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt := &BackTest{ + Statistic: &statistics.Statistic{}, + Funding: &funding.FundManager{}, + Portfolio: pt, + Exchange: &exchange.Exchange{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + } + cp := currency.NewPair(currency.BTC, currency.USD) + a := asset.Futures + de := &evkline.Kline{ + Base: event.Base{Exchange: testExchange, + AssetType: a, + CurrencyPair: cp}, + } + err = bt.Statistic.SetupEventForTime(de) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + ev := &fill.Fill{ + Base: de.Base, + } + em := engine.SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + exch.SetDefaults() + cfg, err := exch.GetDefaultConfig() + if err != nil { + t.Fatal(err) + } + err = exch.Setup(cfg) + if err != nil { + t.Fatal(err) + } + em.Add(exch) + f, err := funding.SetupFundingManager(em, false, true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + b, err := funding.CreateItem(testExchange, a, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + quote, err := funding.CreateItem(testExchange, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + pair, err := funding.CreateCollateral(b, quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + err = f.AddItem(b) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = f.AddItem(quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + spotBase, err := funding.CreateItem(testExchange, asset.Spot, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + spotQuote, err := funding.CreateItem(testExchange, asset.Spot, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + spotPair, err := funding.CreatePair(spotBase, spotQuote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = f.AddPair(spotPair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + bt.Funding = f + err = pt.SetupCurrencySettingsMap(&exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + bt.Exchange.SetExchangeAssetCurrencySettings(a, cp, &exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + ev.Direction = gctorder.Short + err = bt.Statistic.SetEventForOffset(ev) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + tt := time.Now() + bt.Datas.Setup() + k := kline.DataFromKline{ + Item: gctkline.Item{ + Exchange: testExchange, + Pair: cp, + Asset: a, + Interval: gctkline.FifteenMin, + Candles: []gctkline.Candle{{ + Time: tt, + Open: 1337, + High: 1337, + Low: 1337, + Close: 1337, + Volume: 1337, + }}, + }, + Base: data.Base{}, + RangeHolder: &gctkline.IntervalRangeHolder{ + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + Ranges: []gctkline.IntervalRange{ + { + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + Intervals: []gctkline.IntervalData{ + { + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + HasData: true, + }, + }, + }, + }, + }, + } + err = k.Load() + if err != nil { + t.Error(err) + } + + bt.Datas.SetDataForCurrency(testExchange, a, cp, &k) + err = bt.processFillEvent(ev, pair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } +} + +func TestProcessFuturesFillEvent(t *testing.T) { + t.Parallel() + var expectedError error + pt, err := portfolio.Setup(&size.Size{}, &risk.Risk{}, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt := &BackTest{ + Statistic: &statistics.Statistic{}, + Funding: &funding.FundManager{}, + Portfolio: pt, + Exchange: &exchange.Exchange{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + } + cp := currency.NewPair(currency.BTC, currency.USD) + a := asset.Futures + de := &evkline.Kline{ + Base: event.Base{Exchange: testExchange, + AssetType: a, + CurrencyPair: cp}, + } + err = bt.Statistic.SetupEventForTime(de) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + ev := &fill.Fill{ + Base: de.Base, + } + em := engine.SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + exch.SetDefaults() + cfg, err := exch.GetDefaultConfig() + if err != nil { + t.Fatal(err) + } + err = exch.Setup(cfg) + if err != nil { + t.Fatal(err) + } + em.Add(exch) + f, err := funding.SetupFundingManager(em, false, true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + b, err := funding.CreateItem(testExchange, a, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + quote, err := funding.CreateItem(testExchange, a, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + pair, err := funding.CreateCollateral(b, quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + err = f.AddItem(b) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = f.AddItem(quote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + spotBase, err := funding.CreateItem(testExchange, asset.Spot, cp.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + spotQuote, err := funding.CreateItem(testExchange, asset.Spot, cp.Quote, decimal.NewFromInt(1337), decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + spotPair, err := funding.CreatePair(spotBase, spotQuote) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + err = f.AddPair(spotPair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + bt.exchangeManager = em + bt.Funding = f + err = pt.SetupCurrencySettingsMap(&exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + + bt.Exchange.SetExchangeAssetCurrencySettings(a, cp, &exchange.Settings{ + Exchange: exch, + Pair: cp, + Asset: a, + }) + ev.Direction = gctorder.Short + err = bt.Statistic.SetEventForOffset(ev) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } + tt := time.Now() + bt.Datas.Setup() + k := kline.DataFromKline{ + Item: gctkline.Item{ + Exchange: testExchange, + Pair: cp, + Asset: a, + Interval: gctkline.FifteenMin, + Candles: []gctkline.Candle{{ + Time: tt, + Open: 1337, + High: 1337, + Low: 1337, + Close: 1337, + Volume: 1337, + }}, + }, + Base: data.Base{}, + RangeHolder: &gctkline.IntervalRangeHolder{ + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + Ranges: []gctkline.IntervalRange{ + { + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + Intervals: []gctkline.IntervalData{ + { + Start: gctkline.CreateIntervalTime(tt), + End: gctkline.CreateIntervalTime(tt.Add(gctkline.FifteenMin.Duration())), + HasData: true, + }, + }, + }, + }, + }, + } + err = k.Load() + if err != nil { + t.Error(err) + } + ev.Order = &gctorder.Detail{ + Exchange: testExchange, + AssetType: ev.AssetType, + Pair: cp, + Amount: 1, + Price: 1, + Side: gctorder.Short, + ID: "1", + Date: time.Now(), + } + bt.Datas.SetDataForCurrency(testExchange, a, cp, &k) + err = bt.processFuturesFillEvent(ev, pair) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v'", err, expectedError) + } +} diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 44d2b8d49f2..b74c3f4da56 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -62,14 +62,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } f.ExchangeFee = cs.ExchangeFee f.Direction = o.GetDirection() - highStr := data.StreamHigh() - high := highStr[len(highStr)-1] - lowStr := data.StreamLow() - low := lowStr[len(lowStr)-1] - - volStr := data.StreamVol() - volume := volStr[len(volStr)-1] var adjustedPrice, amount decimal.Decimal if cs.UseRealOrders { @@ -108,6 +101,14 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.VolumeAdjustedPrice = f.ClosePrice amount = f.Amount } else { + highStr := data.StreamHigh() + high := highStr[len(highStr)-1] + + lowStr := data.StreamLow() + low := lowStr[len(lowStr)-1] + + volStr := data.StreamVol() + volume := volStr[len(volStr)-1] f.VolumeAdjustedPrice, amount = ensureOrderFitsWithinHLV(f.ClosePrice, f.Amount, high, low, volume) if !amount.Equal(f.GetAmount()) { f.AppendReasonf("Order size shrunk from %v to %v to fit candle", f.Amount, amount) From 22d0b517766f908bf91f86da8e33e2f63e5bd7b5 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 19 Apr 2022 16:34:00 +1000 Subject: [PATCH 122/171] Funding, strategy, report test coverage --- backtester/config/config.go | 8 +- backtester/engine/backtest_test.go | 28 + backtester/engine/setup.go | 2 +- .../portfolio/holdings/holdings_test.go | 4 +- .../eventhandlers/portfolio/portfolio_test.go | 2 +- .../statistics/statistics_types.go | 2 +- .../ftxcashandcarry/ftxcashandcarry.go | 123 ++-- .../ftxcashandcarry/ftxcashandcarry_test.go | 303 +++++++- .../ftxcashandcarry/ftxcashandcarry_types.go | 4 +- backtester/eventtypes/order/order_test.go | 71 +- .../{collateral.go => collateralpair.go} | 106 +-- backtester/funding/collateralpair_test.go | 330 +++++++++ backtester/funding/funding.go | 30 +- backtester/funding/funding_test.go | 677 +++++++----------- backtester/funding/funding_types.go | 8 +- backtester/funding/item.go | 6 +- backtester/funding/item_test.go | 154 ++++ backtester/funding/{pair.go => spotpair.go} | 64 +- backtester/funding/spotpair_test.go | 345 +++++++++ backtester/report/chart.go | 60 +- backtester/report/chart_test.go | 83 +++ backtester/report/report.go | 10 +- backtester/report/report_types.go | 8 + 23 files changed, 1753 insertions(+), 675 deletions(-) rename backtester/funding/{collateral.go => collateralpair.go} (65%) create mode 100644 backtester/funding/collateralpair_test.go create mode 100644 backtester/funding/item_test.go rename backtester/funding/{pair.go => spotpair.go} (71%) create mode 100644 backtester/funding/spotpair_test.go create mode 100644 backtester/report/chart_test.go diff --git a/backtester/config/config.go b/backtester/config/config.go index 81fb0bbbbd3..e8bc6849787 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -269,8 +269,12 @@ func (c *Config) PrintSetting() { c.CurrencySettings[i].Quote) } } - log.Infof(common.SubLoggers[common.Config], "Maker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) - log.Infof(common.SubLoggers[common.Config], "Taker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) + if c.CurrencySettings[i].TakerFee != nil { + log.Infof(common.SubLoggers[common.Config], "Maker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) + } + if c.CurrencySettings[i].MakerFee != nil { + log.Infof(common.SubLoggers[common.Config], "Taker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) + } log.Infof(common.SubLoggers[common.Config], "Minimum slippage percent %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) log.Infof(common.SubLoggers[common.Config], "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) log.Infof(common.SubLoggers[common.Config], "Buy rules: %+v", c.CurrencySettings[i].BuySide) diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index d3bb94d84fd..b8d1b05ed7f 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -90,6 +90,12 @@ func TestNewFromConfig(t *testing.T) { Base: "test", Quote: "test", }, + { + ExchangeName: testExchange, + Base: "BTC", + Quote: "0624", + Asset: asset.Futures.String(), + }, } _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, engine.ErrExchangeNotFound) { @@ -143,6 +149,28 @@ func TestNewFromConfig(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } + + cfg.FundingSettings.UseExchangeLevelFunding = true + cfg.FundingSettings.ExchangeLevelFunding = []config.ExchangeLevelFunding{ + { + ExchangeName: testExchange, + Asset: asset.Spot.String(), + Currency: currency.BTC.String(), + InitialFunds: leet, + TransferFee: leet, + }, + { + ExchangeName: testExchange, + Asset: asset.Futures.String(), + Currency: currency.BTC.String(), + InitialFunds: leet, + TransferFee: leet, + }, + } + _, err = NewFromConfig(cfg, "", "", false) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } } func TestLoadDataAPI(t *testing.T) { diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index c5c037123a2..892d3a143cf 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -328,7 +328,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if err != nil { return nil, err } - var pair *funding.Pair + var pair *funding.SpotPair pair, err = funding.CreatePair(baseItem, quoteItem) if err != nil { return nil, err diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index f9a6e4dba81..d438b8d1138 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -21,7 +21,7 @@ const ( testExchange = "binance" ) -func pair(t *testing.T) *funding.Pair { +func pair(t *testing.T) *funding.SpotPair { t.Helper() b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero) if err != nil { @@ -38,7 +38,7 @@ func pair(t *testing.T) *funding.Pair { return p } -func collateral(t *testing.T) *funding.Collateral { +func collateral(t *testing.T) *funding.CollateralPair { t.Helper() b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.Zero, decimal.Zero) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index ab46b9c77fe..b963acd5504 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -821,7 +821,7 @@ func TestTrackFuturesOrder(t *testing.T) { if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v", err, expectedError) } - fundPair := &funding.Pair{} + fundPair := &funding.SpotPair{} expectedError = gctorder.ErrSubmissionIsNil _, err = p.TrackFuturesOrder(&fill.Fill{}, fundPair) if !errors.Is(err, expectedError) { diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index b9b90183320..dc9495dfdab 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -234,7 +234,7 @@ type FundingItemStatistics struct { TotalOrders int64 MaxDrawdown Swing HighestCommittedFunds ValueAtTime - // Collateral stats + // CollateralPair stats IsCollateral bool InitialCollateral ValueAtTime FinalCollateral ValueAtTime diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 42118556d85..3a8269835b6 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -52,6 +52,15 @@ var errNotSetup = errors.New("sent incomplete signals") // OnSimultaneousSignals analyses multiple data points simultaneously, allowing flexibility // in allowing a strategy to only place an order for X currency if Y currency's price is Z func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTransferer, p portfolio.Handler) ([]signal.Event, error) { + if len(d) == 0 { + return nil, errNoSignals + } + if f == nil { + return nil, fmt.Errorf("%w missing funding transferer", common.ErrNilArguments) + } + if p == nil { + return nil, fmt.Errorf("%w missing portfolio handler", common.ErrNilArguments) + } var response []signal.Event sortedSignals, err := sortSignals(d) if err != nil { @@ -76,8 +85,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra futuresSignal.SetDirection(common.DoNothing) fp := v.futureSignal.Latest().GetClosePrice() sp := v.spotSignal.Latest().GetClosePrice() - hundred := decimal.NewFromInt(100) - diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(hundred) + diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(decimal.NewFromInt(100)) futuresSignal.AppendReasonf("Futures Spot Difference: %v%%", diffBetweenFuturesSpot) if pos != nil && pos[len(pos)-1].Status == order.Open { futuresSignal.AppendReasonf("Unrealised PNL: %v %v", pos[len(pos)-1].UnrealisedPNL, pos[len(pos)-1].Underlying) @@ -88,60 +96,71 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra response = append(response, &spotSignal, &futuresSignal) continue } - - switch { - case len(pos) == 0: - // check to see if order is appropriate to action - spotSignal.SetPrice(v.spotSignal.Latest().GetClosePrice()) - spotSignal.AppendReasonf("Signalling purchase of %v", spotSignal.Pair()) - // first the spot purchase - spotSignal.SetDirection(order.Buy) - // second the futures purchase, using the newly acquired asset - // as collateral to short - futuresSignal.SetDirection(order.Short) - futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) - futuresSignal.AppendReason("Shorting to perform cash and carry") - futuresSignal.CollateralCurrency = spotSignal.CurrencyPair.Base - spotSignal.AppendReasonf("Signalling shorting of %v", futuresSignal.Pair()) - // set the FillDependentEvent to use the futures signal - // as the futures signal relies on a completed spot order purchase - // to use as collateral - spotSignal.FillDependentEvent = &futuresSignal - // only appending spotSignal as futuresSignal will be raised later - response = append(response, &spotSignal) - case len(pos) > 0 && - pos[len(pos)-1].Status == order.Open && - v.futureSignal.IsLastEvent(): - futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReason("Closing position on last event") - response = append(response, &spotSignal, &futuresSignal) - case len(pos) > 0 && - pos[len(pos)-1].Status == order.Open && - pos[len(pos)-1].OpeningPrice.GreaterThan(futuresSignal.ClosePrice) && - s.alwaysCloseOnProfit: - futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReasonf("Closing position. Always close on profit. UPNL %v", pos[len(pos)-1].UnrealisedPNL) - response = append(response, &spotSignal, &futuresSignal) - case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && - diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): - futuresSignal.SetDirection(common.ClosePosition) - futuresSignal.AppendReasonf("Closing position. Threshold %v", s.closeShortDistancePercentage) - response = append(response, &spotSignal, &futuresSignal) - case len(pos) > 0 && - pos[len(pos)-1].Status == order.Closed && - diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): - futuresSignal.SetDirection(order.Short) - futuresSignal.SetPrice(v.futureSignal.Latest().GetClosePrice()) - futuresSignal.AppendReasonf("Opening position. Threshold %v", s.openShortDistancePercentage) - response = append(response, &spotSignal, &futuresSignal) - default: - response = append(response, &spotSignal, &futuresSignal) + signals, err := s.createSignals(pos, &spotSignal, &futuresSignal, diffBetweenFuturesSpot, v.futureSignal.IsLastEvent()) + if err != nil { + return nil, err } + response = append(response, signals...) + } + return response, nil +} + +func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresSignal *signal.Signal, diffBetweenFuturesSpot decimal.Decimal, isLastEvent bool) ([]signal.Event, error) { + if spotSignal == nil { + return nil, fmt.Errorf("%w missing spot signal", common.ErrNilArguments) + } + if futuresSignal == nil { + return nil, fmt.Errorf("%w missing futures signal", common.ErrNilArguments) + } + var response []signal.Event + switch { + case len(pos) == 0: + // check to see if order is appropriate to action + spotSignal.SetPrice(spotSignal.ClosePrice) + spotSignal.AppendReasonf("Signalling purchase of %v", spotSignal.Pair()) + // first the spot purchase + spotSignal.SetDirection(order.Buy) + // second the futures purchase, using the newly acquired asset + // as collateral to short + futuresSignal.SetDirection(order.Short) + futuresSignal.SetPrice(futuresSignal.ClosePrice) + futuresSignal.AppendReason("Shorting to perform cash and carry") + futuresSignal.CollateralCurrency = spotSignal.CurrencyPair.Base + spotSignal.AppendReasonf("Signalling shorting of %v", futuresSignal.Pair()) + // set the FillDependentEvent to use the futures signal + // as the futures signal relies on a completed spot order purchase + // to use as collateral + spotSignal.FillDependentEvent = futuresSignal + // only appending spotSignal as futuresSignal will be raised later + response = append(response, spotSignal) + case len(pos) > 0 && + pos[len(pos)-1].Status == order.Open && + isLastEvent: + futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.AppendReason("Closing position on last event") + response = append(response, spotSignal, futuresSignal) + case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && + diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): + futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.AppendReasonf("Closing position. Threshold %v", s.closeShortDistancePercentage) + response = append(response, spotSignal, futuresSignal) + case len(pos) > 0 && + pos[len(pos)-1].Status == order.Closed && + diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): + futuresSignal.SetDirection(order.Short) + futuresSignal.SetPrice(futuresSignal.ClosePrice) + futuresSignal.AppendReasonf("Opening position. Threshold %v", s.openShortDistancePercentage) + response = append(response, spotSignal, futuresSignal) + default: + response = append(response, spotSignal, futuresSignal) } return response, nil } func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { + if len(d) == 0 { + return nil, errNoSignals + } var response = make(map[currency.Pair]cashCarrySignals) for i := range d { l := d[i].Latest() @@ -167,7 +186,6 @@ func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { for _, v := range response { if v.futureSignal == nil { return nil, errNotSetup - } if v.spotSignal == nil { return nil, errNotSetup @@ -205,7 +223,4 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro func (s *Strategy) SetDefaults() { s.openShortDistancePercentage = decimal.Zero s.closeShortDistancePercentage = decimal.Zero - // TODO set false - s.onlyCloseOnProfit = false - s.alwaysCloseOnProfit = false } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 8bb8fc4ae06..98ca8873ed2 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -2,20 +2,24 @@ package ftxcashandcarry import ( "errors" - "strings" "testing" "time" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" - "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" + datakline "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" eventkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" + + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" + "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) func TestName(t *testing.T) { @@ -91,41 +95,6 @@ func TestOnSignal(t *testing.T) { } } -func TestOnSignals(t *testing.T) { - t.Parallel() - s := Strategy{} - dInsert := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - exch := "ftx" - a := asset.Spot - p := currency.NewPair(currency.BTC, currency.USDT) - d := data.Base{} - d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ - Exchange: exch, - Time: dInsert, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, - Open: decimal.NewFromInt(1337), - Close: decimal.NewFromInt(1337), - Low: decimal.NewFromInt(1337), - High: decimal.NewFromInt(1337), - Volume: decimal.NewFromInt(1337), - }}) - d.Next() - da := &kline.DataFromKline{ - Item: gctkline.Item{}, - Base: d, - RangeHolder: &gctkline.IntervalRangeHolder{}, - } - _, err := s.OnSimultaneousSignals([]data.Handler{da}, nil, nil) - if !strings.Contains(err.Error(), errNotSetup.Error()) { - // common.Errs type doesn't keep type - t.Errorf("received: %v, expected: %v", err, errNotSetup) - } -} - func TestSetDefaults(t *testing.T) { t.Parallel() s := Strategy{} @@ -160,7 +129,7 @@ func TestSortSignals(t *testing.T) { Volume: decimal.NewFromInt(1337), }}) d.Next() - da := &kline.DataFromKline{ + da := &datakline.DataFromKline{ Item: gctkline.Item{}, Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, @@ -186,9 +155,265 @@ func TestSortSignals(t *testing.T) { Volume: decimal.NewFromInt(1337), }}) d.Next() - da = &kline.DataFromKline{ + da = &datakline.DataFromKline{ Item: gctkline.Item{}, Base: d, RangeHolder: &gctkline.IntervalRangeHolder{}, } } + +func TestCreateSignals(t *testing.T) { + t.Parallel() + s := Strategy{} + var expectedError = common.ErrNilArguments + _, err := s.createSignals(nil, nil, nil, decimal.Zero, false) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + spotSignal := &signal.Signal{ + Base: event.Base{AssetType: asset.Spot}, + } + _, err = s.createSignals(nil, spotSignal, nil, decimal.Zero, false) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + // case len(pos) == 0: + expectedError = nil + futuresSignal := &signal.Signal{ + Base: event.Base{AssetType: asset.Futures}, + } + resp, err := s.createSignals(nil, spotSignal, futuresSignal, decimal.Zero, false) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 1 { + t.Errorf("received '%v' expected '%v", len(resp), 1) + } + if resp[0].GetAssetType() != asset.Spot { + t.Errorf("received '%v' expected '%v", resp[0].GetAssetType(), asset.Spot) + } + + // case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && + // diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): + pos := []gctorder.PositionStats{ + { + Status: gctorder.Open, + }, + } + resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.Zero, false) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 2 { + t.Errorf("received '%v' expected '%v", len(resp), 2) + } + caseTested := false + for i := range resp { + if resp[i].GetAssetType().IsFutures() { + if resp[i].GetDirection() != common.ClosePosition { + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) + } + caseTested = true + } else { + + } + } + if !caseTested { + t.Fatal("unhandled issue in test scenario") + } + + // case len(pos) > 0 && + // pos[len(pos)-1].Status == order.Open && + // isLastEvent: + resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.Zero, true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 2 { + t.Errorf("received '%v' expected '%v", len(resp), 2) + } + caseTested = false + for i := range resp { + if resp[i].GetAssetType().IsFutures() { + if resp[i].GetDirection() != common.ClosePosition { + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) + } + caseTested = true + } else { + + } + } + if !caseTested { + t.Fatal("unhandled issue in test scenario") + } + // case len(pos) > 0 && + // pos[len(pos)-1].Status == order.Closed && + // diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): + pos[0].Status = gctorder.Closed + resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.NewFromInt(1337), true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 2 { + t.Errorf("received '%v' expected '%v", len(resp), 2) + } + caseTested = false + for i := range resp { + if resp[i].GetAssetType().IsFutures() { + if resp[i].GetDirection() != gctorder.Short { + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) + } + caseTested = true + } else { + + } + } + if !caseTested { + t.Fatal("unhandled issue in test scenario") + } + + // default: + pos[0].Status = gctorder.UnknownStatus + resp, err = s.createSignals(pos, spotSignal, futuresSignal, decimal.NewFromInt(1337), true) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 2 { + t.Errorf("received '%v' expected '%v", len(resp), 2) + } +} + +// funderino overrides default implementation +type funderino struct { + funding.FundManager + hasBeenLiquidated bool +} + +// HasExchangeBeenLiquidated overrides default implementation +func (f funderino) HasExchangeBeenLiquidated(_ common.EventHandler) bool { + return f.hasBeenLiquidated +} + +// portfolerino overrides default implementation +type portfolerino struct { + portfolio.Portfolio +} + +// GetPositions overrides default implementation +func (p portfolerino) GetPositions(common.EventHandler) ([]gctorder.PositionStats, error) { + return []gctorder.PositionStats{ + { + Exchange: exchangeName, + Asset: asset.Spot, + Pair: currency.NewPair(currency.BTC, currency.USD), + Underlying: currency.BTC, + CollateralCurrency: currency.USD, + }, + }, nil +} + +func TestOnSimultaneousSignals(t *testing.T) { + t.Parallel() + s := Strategy{} + var expectedError = errNoSignals + _, err := s.OnSimultaneousSignals(nil, nil, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + expectedError = common.ErrNilArguments + cp := currency.NewPair(currency.BTC, currency.USD) + d := &datakline.DataFromKline{ + Base: data.Base{}, + Item: gctkline.Item{ + Exchange: exchangeName, + Asset: asset.Spot, + Pair: cp, + UnderlyingPair: currency.NewPair(currency.BTC, currency.USD), + }, + } + tt := time.Now() + d.SetStream([]common.DataEventHandler{&eventkline.Kline{ + Base: event.Base{ + Exchange: exchangeName, + Time: tt, + Interval: gctkline.OneDay, + CurrencyPair: cp, + AssetType: asset.Spot, + }, + Open: decimal.NewFromInt(1337), + Close: decimal.NewFromInt(1337), + Low: decimal.NewFromInt(1337), + High: decimal.NewFromInt(1337), + Volume: decimal.NewFromInt(1337), + }}) + + d.Next() + signals := []data.Handler{ + d, + } + f := &funderino{} + _, err = s.OnSimultaneousSignals(signals, f, nil) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + p := &portfolerino{} + expectedError = errNotSetup + _, err = s.OnSimultaneousSignals(signals, f, p) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + + expectedError = nil + d2 := &datakline.DataFromKline{ + Base: data.Base{}, + Item: gctkline.Item{ + Exchange: exchangeName, + Asset: asset.Futures, + Pair: cp, + UnderlyingPair: cp, + }, + } + d2.SetStream([]common.DataEventHandler{&eventkline.Kline{ + Base: event.Base{ + Exchange: exchangeName, + Time: tt, + Interval: gctkline.OneDay, + CurrencyPair: cp, + AssetType: asset.Futures, + UnderlyingPair: cp, + }, + Open: decimal.NewFromInt(1337), + Close: decimal.NewFromInt(1337), + Low: decimal.NewFromInt(1337), + High: decimal.NewFromInt(1337), + Volume: decimal.NewFromInt(1337), + }}) + d2.Next() + signals = []data.Handler{ + d, + d2, + } + resp, err := s.OnSimultaneousSignals(signals, f, p) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 2 { + t.Errorf("received '%v' expected '%v", len(resp), 2) + } + + f.hasBeenLiquidated = true + resp, err = s.OnSimultaneousSignals(signals, f, p) + if !errors.Is(err, expectedError) { + t.Errorf("received '%v' expected '%v", err, expectedError) + } + if len(resp) != 2 { + t.Fatalf("received '%v' expected '%v", len(resp), 2) + } + if resp[0].GetDirection() != common.DoNothing { + t.Errorf("received '%v' expected '%v", resp[0].GetDirection(), common.DoNothing) + } +} diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go index 271f7421932..4d336185c22 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go @@ -14,12 +14,12 @@ const ( exchangeName = "ftx" openShortDistancePercentageString = "openShortDistancePercentage" closeShortDistancePercentageString = "closeShortDistancePercentage" - onlyCloseOnProfitString = "onlyCloseOnProfit" ) var ( errFuturesOnly = errors.New("can only work with futures") errOnlyFTXSupported = errors.New("only FTX supported for this strategy") + errNoSignals = errors.New("no data signals to process") ) // Strategy is an implementation of the Handler interface @@ -27,6 +27,4 @@ type Strategy struct { base.Strategy openShortDistancePercentage decimal.Decimal closeShortDistancePercentage decimal.Decimal - onlyCloseOnProfit bool - alwaysCloseOnProfit bool } diff --git a/backtester/eventtypes/order/order_test.go b/backtester/eventtypes/order/order_test.go index c538cf93cf0..56978b3c02f 100644 --- a/backtester/eventtypes/order/order_test.go +++ b/backtester/eventtypes/order/order_test.go @@ -5,6 +5,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/currency" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -39,7 +40,7 @@ func TestSetAmount(t *testing.T) { } } -func TestPair(t *testing.T) { +func TestIsEmpty(t *testing.T) { t.Parallel() o := Order{ Base: event.Base{ @@ -106,3 +107,71 @@ func TestIsLiquidating(t *testing.T) { t.Error("expected true") } } + +func TestGetBuyLimit(t *testing.T) { + t.Parallel() + k := Order{ + BuyLimit: decimal.NewFromInt(1337), + } + bl := k.GetBuyLimit() + if !bl.Equal(decimal.NewFromInt(1337)) { + t.Errorf("received '%v' expected '%v'", bl, decimal.NewFromInt(1337)) + } +} + +func TestGetSellLimit(t *testing.T) { + t.Parallel() + k := Order{ + SellLimit: decimal.NewFromInt(1337), + } + sl := k.GetSellLimit() + if !sl.Equal(decimal.NewFromInt(1337)) { + t.Errorf("received '%v' expected '%v'", sl, decimal.NewFromInt(1337)) + } +} + +func TestPair(t *testing.T) { + t.Parallel() + cp := currency.NewPair(currency.BTC, currency.USDT) + k := Order{ + Base: event.Base{ + CurrencyPair: cp, + }, + } + p := k.Pair() + if !p.Equal(cp) { + t.Errorf("received '%v' expected '%v'", p, cp) + } +} + +func TestGetStatus(t *testing.T) { + t.Parallel() + k := Order{ + Status: gctorder.UnknownStatus, + } + s := k.GetStatus() + if s != gctorder.UnknownStatus { + t.Errorf("received '%v' expected '%v'", s, gctorder.UnknownStatus) + } +} + +func TestGetFillDependentEvent(t *testing.T) { + t.Parallel() + k := Order{ + FillDependentEvent: &signal.Signal{Amount: decimal.NewFromInt(1337)}, + } + fde := k.GetFillDependentEvent() + if !fde.GetAmount().Equal(decimal.NewFromInt(1337)) { + t.Errorf("received '%v' expected '%v'", fde, decimal.NewFromInt(1337)) + } +} +func TestIsClosingPosition(t *testing.T) { + t.Parallel() + k := Order{ + ClosingPosition: true, + } + s := k.IsClosingPosition() + if !s { + t.Errorf("received '%v' expected '%v'", s, true) + } +} diff --git a/backtester/funding/collateral.go b/backtester/funding/collateralpair.go similarity index 65% rename from backtester/funding/collateral.go rename to backtester/funding/collateralpair.go index 483468c4c78..1a9d31c3370 100644 --- a/backtester/funding/collateral.go +++ b/backtester/funding/collateralpair.go @@ -12,19 +12,21 @@ import ( // collateral related errors var ( - ErrNotCollateral = errors.New("not a collateral pair") - ErrIsCollateral = errors.New("is collateral pair") - ErrNilPair = errors.New("nil pair") - errPositiveOnly = errors.New("reduces the amount by subtraction, positive numbers only") + // ErrNotPair is returned when a user requests funding pair details when it is a collateral pair + ErrNotPair = errors.New("not a funding pair") + ErrIsCollateral = errors.New("is collateral pair") + ErrNilPair = errors.New("nil pair") + errUnhandled = errors.New("unhandled scenario") + errPositiveOnly = errors.New("reduces the amount by subtraction, positive numbers only") ) // CanPlaceOrder checks if there is any collateral to spare -func (c *Collateral) CanPlaceOrder(_ gctorder.Side) bool { +func (c *CollateralPair) CanPlaceOrder(_ gctorder.Side) bool { return c.collateral.CanPlaceOrder() } // TakeProfit handles both the reduction of contracts and the change in collateral -func (c *Collateral) TakeProfit(contracts, positionReturns decimal.Decimal) error { +func (c *CollateralPair) TakeProfit(contracts, positionReturns decimal.Decimal) error { err := c.contract.ReduceContracts(contracts) if err != nil { return err @@ -33,37 +35,27 @@ func (c *Collateral) TakeProfit(contracts, positionReturns decimal.Decimal) erro } // ContractCurrency returns the contract currency -func (c *Collateral) ContractCurrency() currency.Code { +func (c *CollateralPair) ContractCurrency() currency.Code { return c.contract.currency } // CollateralCurrency returns collateral currency -func (c *Collateral) CollateralCurrency() currency.Code { +func (c *CollateralPair) CollateralCurrency() currency.Code { return c.collateral.currency } // InitialFunds returns initial funds of collateral -func (c *Collateral) InitialFunds() decimal.Decimal { +func (c *CollateralPair) InitialFunds() decimal.Decimal { return c.collateral.initialFunds } // AvailableFunds returns available funds of collateral -func (c *Collateral) AvailableFunds() decimal.Decimal { +func (c *CollateralPair) AvailableFunds() decimal.Decimal { return c.collateral.available } -// GetPairReader returns an error because collateral isn't a pair -func (c *Collateral) GetPairReader() (IPairReader, error) { - return nil, fmt.Errorf("could not return pair reader for %v %v %v %v %w", c.contract.exchange, c.collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) -} - -// GetCollateralReader returns a collateral reader interface of Collateral -func (c *Collateral) GetCollateralReader() (ICollateralReader, error) { - return c, nil -} - // UpdateContracts adds or subtracts contracts based on order direction -func (c *Collateral) UpdateContracts(s gctorder.Side, amount decimal.Decimal) error { +func (c *CollateralPair) UpdateContracts(s gctorder.Side, amount decimal.Decimal) error { switch { case c.currentDirection == nil: c.currentDirection = &s @@ -73,13 +65,13 @@ func (c *Collateral) UpdateContracts(s gctorder.Side, amount decimal.Decimal) er case *c.currentDirection != s: return c.contract.ReduceContracts(amount) default: - return errors.New("woah nelly") + return errUnhandled } } // ReleaseContracts lowers the amount of available contracts -func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { - if amount.LessThan(decimal.Zero) { +func (c *CollateralPair) ReleaseContracts(amount decimal.Decimal) error { + if amount.LessThanOrEqual(decimal.Zero) { return fmt.Errorf("release %w", errPositiveOnly) } if c.contract.available.LessThan(amount) { @@ -89,23 +81,8 @@ func (c *Collateral) ReleaseContracts(amount decimal.Decimal) error { return nil } -// FundReader returns a fund reader interface of collateral -func (c *Collateral) FundReader() IFundReader { - return c -} - -// FundReserver returns a fund reserver interface of Collateral -func (c *Collateral) FundReserver() IFundReserver { - return c -} - -// PairReleaser returns an error as there is no such thing for collateral -func (c *Collateral) PairReleaser() (IPairReleaser, error) { - return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.contract.exchange, c.collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) -} - // Reserve reserves or releases collateral based on order side -func (c *Collateral) Reserve(amount decimal.Decimal, side gctorder.Side) error { +func (c *CollateralPair) Reserve(amount decimal.Decimal, side gctorder.Side) error { switch side { case gctorder.Long, gctorder.Short: return c.collateral.Reserve(amount) @@ -121,28 +98,53 @@ func (c *Collateral) Reserve(amount decimal.Decimal, side gctorder.Side) error { } } +// Liquidate kills your funds and future +func (c *CollateralPair) Liquidate() { + c.collateral.available = decimal.Zero + c.collateral.reserved = decimal.Zero + c.contract.available = decimal.Zero + c.contract.reserved = decimal.Zero + c.currentDirection = nil +} + +// CurrentHoldings returns available contract holdings +func (c *CollateralPair) CurrentHoldings() decimal.Decimal { + return c.contract.available +} + +// FundReader returns a fund reader interface of collateral +func (c *CollateralPair) FundReader() IFundReader { + return c +} + +// FundReserver returns a fund reserver interface of CollateralPair +func (c *CollateralPair) FundReserver() IFundReserver { + return c +} + +// PairReleaser returns an error as there is no such thing for collateral +func (c *CollateralPair) PairReleaser() (IPairReleaser, error) { + return nil, fmt.Errorf("could not get pair releaser for %v %v %v %v %w", c.contract.exchange, c.collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) +} + // CollateralReleaser returns an ICollateralReleaser to interact with // collateral -func (c *Collateral) CollateralReleaser() (ICollateralReleaser, error) { +func (c *CollateralPair) CollateralReleaser() (ICollateralReleaser, error) { return c, nil } // FundReleaser returns an IFundReleaser to interact with // collateral -func (c *Collateral) FundReleaser() IFundReleaser { +func (c *CollateralPair) FundReleaser() IFundReleaser { return c } -// Liquidate kills your funds and future -func (c *Collateral) Liquidate() { - c.collateral.available = decimal.Zero - c.collateral.reserved = decimal.Zero - c.contract.available = decimal.Zero - c.contract.reserved = decimal.Zero - c.currentDirection = nil +// GetPairReader returns an error because collateral isn't a pair +func (c *CollateralPair) GetPairReader() (IPairReader, error) { + return nil, fmt.Errorf("could not return pair reader for %v %v %v %v %w", c.contract.exchange, c.collateral.asset, c.ContractCurrency(), c.CollateralCurrency(), ErrNotPair) } -// CurrentHoldings returns available contract holdings -func (c *Collateral) CurrentHoldings() decimal.Decimal { - return c.contract.available +// GetCollateralReader returns a collateral reader interface of CollateralPair +func (c *CollateralPair) GetCollateralReader() (ICollateralReader, error) { + return c, nil } diff --git a/backtester/funding/collateralpair_test.go b/backtester/funding/collateralpair_test.go new file mode 100644 index 00000000000..74d17bd0a73 --- /dev/null +++ b/backtester/funding/collateralpair_test.go @@ -0,0 +1,330 @@ +package funding + +import ( + "errors" + "testing" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +func TestCollateralCanPlaceOrder(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{available: decimal.NewFromInt(1337)}, + } + if !c.CanPlaceOrder("") { + t.Error("expected true") + } +} + +func TestCollateralTakeProfit(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{ + asset: asset.Futures, + isCollateral: true, + }, + contract: &Item{asset: asset.Futures, + available: decimal.NewFromInt(1), + }, + } + var expectedError error + err := c.TakeProfit(decimal.NewFromInt(1), decimal.NewFromInt(1)) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralCollateralCurrency(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{currency: currency.DOGE}, + } + doge := c.CollateralCurrency() + if !doge.Equal(currency.DOGE) { + t.Errorf("recevied '%v' expected '%v'", doge, currency.DOGE) + } +} + +func TestCollateralContractCurrency(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + contract: &Item{currency: currency.DOGE}, + } + doge := c.ContractCurrency() + if !doge.Equal(currency.DOGE) { + t.Errorf("recevied '%v' expected '%v'", doge, currency.DOGE) + } +} + +func TestCollateralInitialFunds(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{initialFunds: decimal.NewFromInt(1337)}, + } + f := c.InitialFunds() + if !f.Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", f, decimal.NewFromInt(1337)) + } +} + +func TestCollateralAvailableFunds(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{available: decimal.NewFromInt(1337)}, + } + f := c.AvailableFunds() + if !f.Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", f, decimal.NewFromInt(1337)) + } +} + +func TestCollateralGetPairReader(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + contract: &Item{}, + collateral: &Item{}, + } + expectedError := ErrNotPair + _, err := c.GetPairReader() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralGetCollateralReader(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{available: decimal.NewFromInt(1337)}, + } + var expectedError error + cr, err := c.GetCollateralReader() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if cr != c { + t.Error("expected the same thing") + } +} + +func TestCollateralUpdateContracts(t *testing.T) { + t.Parallel() + b := gctorder.Buy + var expectedError error + c := &CollateralPair{ + collateral: &Item{ + asset: asset.Futures, + isCollateral: true, + }, + contract: &Item{asset: asset.Futures, + available: decimal.Zero, + }, + currentDirection: &b, + } + leet := decimal.NewFromInt(1337) + err := c.UpdateContracts(gctorder.Buy, leet) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if !c.contract.available.Equal(leet) { + t.Errorf("recevied '%v' expected '%v'", c.contract.available, leet) + } + b = gctorder.Sell + err = c.UpdateContracts(gctorder.Buy, leet) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if !c.contract.available.Equal(decimal.Zero) { + t.Errorf("recevied '%v' expected '%v'", c.contract.available, decimal.Zero) + } + + c.currentDirection = nil + err = c.UpdateContracts(gctorder.Buy, leet) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if !c.contract.available.Equal(leet) { + t.Errorf("recevied '%v' expected '%v'", c.contract.available, leet) + } +} + +func TestCollateralReleaseContracts(t *testing.T) { + t.Parallel() + b := gctorder.Buy + c := &CollateralPair{ + collateral: &Item{ + asset: asset.Futures, + isCollateral: true, + }, + contract: &Item{asset: asset.Futures, + available: decimal.Zero, + }, + currentDirection: &b, + } + + expectedError := errPositiveOnly + err := c.ReleaseContracts(decimal.Zero) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + + expectedError = errCannotAllocate + err = c.ReleaseContracts(decimal.NewFromInt(1337)) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + + expectedError = nil + c.contract.available = decimal.NewFromInt(1337) + err = c.ReleaseContracts(decimal.NewFromInt(1337)) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralFundReader(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{available: decimal.NewFromInt(1337)}, + } + cr := c.FundReader() + if cr != c { + t.Error("expected the same thing") + } +} + +func TestCollateralPairReleaser(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{}, + contract: &Item{}, + } + expectedError := ErrNotPair + _, err := c.PairReleaser() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralFundReserver(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{available: decimal.NewFromInt(1337)}, + } + cr := c.FundReserver() + if cr != c { + t.Error("expected the same thing") + } +} + +func TestCollateralCollateralReleaser(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{}, + contract: &Item{}, + } + var expectedError error + _, err := c.CollateralReleaser() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralFundReleaser(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{available: decimal.NewFromInt(1337)}, + } + cr := c.FundReleaser() + if cr != c { + t.Error("expected the same thing") + } +} + +func TestCollateralReserve(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{ + asset: asset.Futures, + isCollateral: true, + available: decimal.NewFromInt(1337), + }, + contract: &Item{asset: asset.Futures, + available: decimal.Zero, + }, + } + var expectedError error + err := c.Reserve(decimal.NewFromInt(1), gctorder.Long) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if !c.collateral.reserved.Equal(decimal.NewFromInt(1)) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1)) + } + if !c.collateral.available.Equal(decimal.NewFromInt(1336)) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1336)) + } + + err = c.Reserve(decimal.NewFromInt(1), gctorder.Short) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if !c.collateral.reserved.Equal(decimal.NewFromInt(2)) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(2)) + } + if !c.collateral.available.Equal(decimal.NewFromInt(1335)) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1335)) + } + + err = c.Reserve(decimal.NewFromInt(2), common.ClosePosition) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if !c.collateral.reserved.Equal(decimal.Zero) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.Zero) + } + if !c.collateral.available.Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1337)) + } + + expectedError = errCannotAllocate + err = c.Reserve(decimal.NewFromInt(2), gctorder.Buy) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralLiquidate(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + collateral: &Item{ + asset: asset.Futures, + isCollateral: true, + available: decimal.NewFromInt(1337), + }, + contract: &Item{asset: asset.Futures, + available: decimal.NewFromInt(1337), + }, + } + c.Liquidate() + if !c.collateral.available.Equal(decimal.Zero) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.available, decimal.Zero) + } + if !c.contract.available.Equal(decimal.Zero) { + t.Errorf("recevied '%v' expected '%v'", c.contract.available, decimal.Zero) + } +} + +func TestCollateralCurrentHoldings(t *testing.T) { + t.Parallel() + c := &CollateralPair{ + contract: &Item{available: decimal.NewFromInt(1337)}, + } + if !c.CurrentHoldings().Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", c.CurrentHoldings(), decimal.NewFromInt(1337)) + } +} diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 9172d1ec107..e8c664148dd 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -253,7 +253,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { // the association allows for the same currency to be used multiple times when // usingExchangeLevelFunding is false. eg BTC-USDT and LTC-USDT do not share the same // USDT level funding -func CreatePair(base, quote *Item) (*Pair, error) { +func CreatePair(base, quote *Item) (*SpotPair, error) { if base == nil { return nil, fmt.Errorf("base %w", common.ErrNilArguments) } @@ -266,14 +266,14 @@ func CreatePair(base, quote *Item) (*Pair, error) { qCopy := *quote bCopy.pairedWith = &qCopy qCopy.pairedWith = &bCopy - return &Pair{base: &bCopy, quote: &qCopy}, nil + return &SpotPair{base: &bCopy, quote: &qCopy}, nil } // CreateCollateral adds two funding items and associates them with one another // the association allows for the same currency to be used multiple times when // usingExchangeLevelFunding is false. eg BTC-USDT and LTC-USDT do not share the same // USDT level funding -func CreateCollateral(contract, collateral *Item) (*Collateral, error) { +func CreateCollateral(contract, collateral *Item) (*CollateralPair, error) { if contract == nil { return nil, fmt.Errorf("base %w", common.ErrNilArguments) } @@ -286,7 +286,7 @@ func CreateCollateral(contract, collateral *Item) (*Collateral, error) { qCopy := *collateral bCopy.pairedWith = &qCopy qCopy.pairedWith = &bCopy - return &Collateral{contract: &bCopy, collateral: &qCopy}, nil + return &CollateralPair{contract: &bCopy, collateral: &qCopy}, nil } // Reset clears all settings @@ -445,7 +445,7 @@ func (f *FundManager) Exists(item *Item) bool { } // AddPair adds a pair to the fund manager if it does not exist -func (f *FundManager) AddPair(p *Pair) error { +func (f *FundManager) AddPair(p *SpotPair) error { if f.Exists(p.base) { return fmt.Errorf("%w %v", ErrAlreadyExists, p.base) } @@ -469,7 +469,7 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (IFundingPair, // GetFundingForEAP This will construct a funding based on the exchange, asset, currency pair func (f *FundManager) getFundingForEAP(exch string, a asset.Item, p currency.Pair) (IFundingPair, error) { if a.IsFutures() { - var collat Collateral + var collat CollateralPair for i := range f.items { if f.items[i].MatchesCurrency(currency.NewCode(p.String())) { collat.contract = f.items[i] @@ -478,7 +478,7 @@ func (f *FundManager) getFundingForEAP(exch string, a asset.Item, p currency.Pai } } } else { - var resp Pair + var resp SpotPair for i := range f.items { if f.items[i].BasicEqual(exch, a, p.Base, p.Quote) { resp.base = f.items[i] @@ -513,6 +513,9 @@ func (f *FundManager) getFundingForEAC(exch string, a asset.Item, c currency.Cod // Liquidate will remove all funding for all items belonging to an exchange func (f *FundManager) Liquidate(ev common.EventHandler) { + if ev == nil { + return + } for i := range f.items { if f.items[i].exchange == ev.GetExchange() { f.items[i].reserved = decimal.Zero @@ -548,6 +551,9 @@ func (f *FundManager) GetAllFunding() []BasicItem { } func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { + if ev == nil { + return common.ErrNilEvent + } exchMap := make(map[string]exchange.IBotExchange) var collateralAmount decimal.Decimal var err error @@ -593,7 +599,11 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { USDPrice: usd, }) } - futureCurrency, futureAsset, err := exchMap[ev.GetExchange()].GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) + exch, ok := exchMap[ev.GetExchange()] + if !ok { + return fmt.Errorf("%v %w", ev.GetExchange(), engine.ErrExchangeNotFound) + } + futureCurrency, futureAsset, err := exch.GetCollateralCurrencyForContract(ev.GetAssetType(), ev.Pair()) if err != nil { return err } @@ -629,9 +639,7 @@ func (f *FundManager) RealisePNL(receivingExchange string, receivingAsset asset. if f.items[i].exchange == receivingExchange && f.items[i].asset == receivingAsset && f.items[i].currency.Equal(receivingCurrency) { - f.items[i].available = f.items[i].available.Add(realisedPNL) - return nil - + return f.items[i].TakeProfit(realisedPNL) } } return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, realisedPNL, receivingExchange, receivingAsset, receivingCurrency) diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index 739ce34301f..42e25b0128a 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -8,22 +8,23 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" - gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) var ( - elite = decimal.NewFromInt(1337) - neg = decimal.NewFromInt(-1) - one = decimal.NewFromInt(1) - exch = "exch" - a = asset.Spot - base = currency.DOGE - quote = currency.XRP - pair = currency.NewPair(base, quote) + elite = decimal.NewFromInt(1337) + neg = decimal.NewFromInt(-1) + one = decimal.NewFromInt(1) + exchName = "exchname" + a = asset.Spot + base = currency.DOGE + quote = currency.XRP + pair = currency.NewPair(base, quote) ) // fakeEvent implements common.EventHandler without @@ -35,7 +36,7 @@ func (f *fakeEvent) SetOffset(int64) {} func (f *fakeEvent) IsEvent() bool { return true } func (f *fakeEvent) GetTime() time.Time { return time.Now() } func (f *fakeEvent) Pair() currency.Pair { return pair } -func (f *fakeEvent) GetExchange() string { return exch } +func (f *fakeEvent) GetExchange() string { return exchName } func (f *fakeEvent) GetInterval() gctkline.Interval { return gctkline.OneMin } func (f *fakeEvent) GetAssetType() asset.Item { return asset.Spot } func (f *fakeEvent) GetReason() string { return "" } @@ -73,7 +74,7 @@ func TestReset(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -166,7 +167,7 @@ func TestAddItem(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -184,11 +185,10 @@ func TestAddItem(t *testing.T) { func TestExists(t *testing.T) { t.Parallel() f := FundManager{} - exists := f.Exists(nil) - if exists { - t.Errorf("received '%v' expected '%v'", exists, false) + if f.Exists(nil) { + t.Errorf("received '%v' expected '%v'", true, false) } - conflictingSingleItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + conflictingSingleItem, err := CreateItem(exchName, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -196,15 +196,14 @@ func TestExists(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - exists = f.Exists(conflictingSingleItem) - if !exists { - t.Errorf("received '%v' expected '%v'", exists, true) + if !f.Exists(conflictingSingleItem) { + t.Errorf("received '%v' expected '%v'", false, true) } - baseItem, err := CreateItem(exch, a, base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exchName, a, quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -216,12 +215,12 @@ func TestExists(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.getFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exchName, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.getFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exchName, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -256,12 +255,11 @@ func TestExists(t *testing.T) { collateralCandles: quoteItem.collateralCandles, } quoteCopy.pairedWith = &baseCopy - exists = f.Exists(&baseCopy) - if !exists { - t.Errorf("received '%v' expected '%v'", exists, true) + if !f.Exists(&baseCopy) { + t.Errorf("received '%v' expected '%v'", false, true) } - currFunds, err := f.getFundingForEAC(exch, a, base) + currFunds, err := f.getFundingForEAC(exchName, a, base) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -273,11 +271,11 @@ func TestExists(t *testing.T) { func TestAddPair(t *testing.T) { t.Parallel() f := FundManager{} - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -289,7 +287,7 @@ func TestAddPair(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.getFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exchName, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -311,11 +309,11 @@ func TestGetFundingForEvent(t *testing.T) { if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -336,11 +334,11 @@ func TestGetFundingForEvent(t *testing.T) { func TestGetFundingForEAC(t *testing.T) { t.Parallel() f := FundManager{} - _, err := f.getFundingForEAC(exch, a, base) + _, err := f.getFundingForEAC(exchName, a, base) if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -349,7 +347,7 @@ func TestGetFundingForEAC(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - fundo, err := f.getFundingForEAC(exch, a, base) + fundo, err := f.getFundingForEAC(exchName, a, base) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -362,15 +360,15 @@ func TestGetFundingForEAC(t *testing.T) { func TestGetFundingForEAP(t *testing.T) { t.Parallel() f := FundManager{} - _, err := f.getFundingForEAP(exch, a, pair) + _, err := f.getFundingForEAP(exchName, a, pair) if !errors.Is(err, ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, ErrFundsNotFound) } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -382,7 +380,7 @@ func TestGetFundingForEAP(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - _, err = f.getFundingForEAP(exch, a, pair) + _, err = f.getFundingForEAP(exchName, a, pair) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -405,364 +403,6 @@ func TestGetFundingForEAP(t *testing.T) { } } -func TestBaseInitialFunds(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - funds := pairItems.BaseInitialFunds() - if !funds.IsZero() { - t.Errorf("received '%v' expected '%v'", funds, baseItem.available) - } -} - -func TestQuoteInitialFunds(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - funds := pairItems.QuoteInitialFunds() - if !funds.Equal(elite) { - t.Errorf("received '%v' expected '%v'", funds, elite) - } -} - -func TestBaseAvailable(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - funds := pairItems.BaseAvailable() - if !funds.IsZero() { - t.Errorf("received '%v' expected '%v'", funds, baseItem.available) - } -} - -func TestQuoteAvailable(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - funds := pairItems.QuoteAvailable() - if !funds.Equal(elite) { - t.Errorf("received '%v' expected '%v'", funds, elite) - } -} - -func TestReservePair(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - err = pairItems.Reserve(decimal.Zero, gctorder.Buy) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = pairItems.Reserve(elite, gctorder.Buy) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - err = pairItems.Reserve(decimal.Zero, gctorder.Sell) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = pairItems.Reserve(elite, gctorder.Sell) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - err = pairItems.Reserve(elite, common.DoNothing) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } -} - -func TestReleasePair(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - err = pairItems.Reserve(decimal.Zero, gctorder.Buy) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = pairItems.Reserve(elite, gctorder.Buy) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - err = pairItems.Reserve(decimal.Zero, gctorder.Sell) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = pairItems.Reserve(elite, gctorder.Sell) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - - err = pairItems.Release(decimal.Zero, decimal.Zero, gctorder.Buy) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = pairItems.Release(elite, decimal.Zero, gctorder.Buy) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - err = pairItems.Release(elite, decimal.Zero, gctorder.Buy) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - - err = pairItems.Release(elite, decimal.Zero, common.DoNothing) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - - err = pairItems.Release(elite, decimal.Zero, gctorder.Sell) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - err = pairItems.Release(decimal.Zero, decimal.Zero, gctorder.Sell) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } -} - -func TestIncreaseAvailablePair(t *testing.T) { - t.Parallel() - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - baseItem.pairedWith = quoteItem - quoteItem.pairedWith = baseItem - pairItems := Pair{base: baseItem, quote: quoteItem} - pairItems.IncreaseAvailable(decimal.Zero, gctorder.Buy) - if !pairItems.quote.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) - } - pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) - if !pairItems.base.available.IsZero() { - t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.base.available) - } - - pairItems.IncreaseAvailable(elite.Neg(), gctorder.Sell) - if !pairItems.quote.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) - } - pairItems.IncreaseAvailable(elite, gctorder.Buy) - if !pairItems.base.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) - } - - pairItems.IncreaseAvailable(elite, common.DoNothing) - if !pairItems.base.available.Equal(elite) { - t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) - } -} - -func TestCanPlaceOrderPair(t *testing.T) { - t.Parallel() - p := Pair{ - base: &Item{}, - quote: &Item{}, - } - if p.CanPlaceOrder(common.DoNothing) { - t.Error("expected false") - } - if p.CanPlaceOrder(gctorder.Buy) { - t.Error("expected false") - } - if p.CanPlaceOrder(gctorder.Sell) { - t.Error("expected false") - } - - p.quote.available = decimal.NewFromInt(32) - if !p.CanPlaceOrder(gctorder.Buy) { - t.Error("expected true") - } - p.base.available = decimal.NewFromInt(32) - if !p.CanPlaceOrder(gctorder.Sell) { - t.Error("expected true") - } -} - -func TestIncreaseAvailable(t *testing.T) { - t.Parallel() - i := Item{} - i.IncreaseAvailable(elite) - if !i.available.Equal(elite) { - t.Errorf("expected %v", elite) - } - i.IncreaseAvailable(decimal.Zero) - i.IncreaseAvailable(neg) - if !i.available.Equal(elite) { - t.Errorf("expected %v", elite) - } -} - -func TestRelease(t *testing.T) { - t.Parallel() - i := Item{} - err := i.Release(decimal.Zero, decimal.Zero) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = i.Release(elite, decimal.Zero) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - i.reserved = elite - err = i.Release(elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - - i.reserved = elite - err = i.Release(elite, one) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - - err = i.Release(neg, decimal.Zero) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = i.Release(elite, neg) - if !errors.Is(err, errNegativeAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errNegativeAmountReceived) - } -} - -func TestReserve(t *testing.T) { - t.Parallel() - i := Item{} - err := i.Reserve(decimal.Zero) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } - err = i.Reserve(elite) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - - i.reserved = elite - err = i.Reserve(elite) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - - i.available = elite - err = i.Reserve(elite) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - - err = i.Reserve(elite) - if !errors.Is(err, errCannotAllocate) { - t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) - } - - err = i.Reserve(neg) - if !errors.Is(err, errZeroAmountReceived) { - t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) - } -} - -func TestMatchesItemCurrency(t *testing.T) { - t.Parallel() - i := Item{} - if i.MatchesItemCurrency(nil) { - t.Errorf("received '%v' expected '%v'", true, false) - } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - if baseItem.MatchesItemCurrency(quoteItem) { - t.Errorf("received '%v' expected '%v'", true, false) - } - if !baseItem.MatchesItemCurrency(baseItem) { - t.Errorf("received '%v' expected '%v'", false, true) - } -} - -func TestMatchesExchange(t *testing.T) { - t.Parallel() - i := Item{} - if i.MatchesExchange(nil) { - t.Errorf("received '%v' expected '%v'", true, false) - } - baseItem, err := CreateItem(exch, a, pair.Base, decimal.Zero, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - if !baseItem.MatchesExchange(quoteItem) { - t.Errorf("received '%v' expected '%v'", false, true) - } - if !baseItem.MatchesExchange(baseItem) { - t.Errorf("received '%v' expected '%v'", false, true) - } -} - func TestGenerateReport(t *testing.T) { t.Parallel() f := FundManager{} @@ -774,7 +414,7 @@ func TestGenerateReport(t *testing.T) { t.Error("expected 0") } item := &Item{ - exchange: exch, + exchange: exchName, initialFunds: decimal.NewFromInt(100), available: decimal.NewFromInt(200), currency: currency.BTC, @@ -794,7 +434,7 @@ func TestGenerateReport(t *testing.T) { f.usingExchangeLevelFunding = true err = f.AddItem(&Item{ - exchange: exch, + exchange: exchName, initialFunds: decimal.NewFromInt(100), available: decimal.NewFromInt(200), currency: currency.USD, @@ -806,7 +446,7 @@ func TestGenerateReport(t *testing.T) { dfk := &kline.DataFromKline{ Item: gctkline.Item{ - Exchange: exch, + Exchange: exchName, Pair: currency.NewPair(currency.BTC, currency.USD), Asset: a, Interval: gctkline.OneHour, @@ -840,25 +480,6 @@ func TestGenerateReport(t *testing.T) { } } -func TestMatchesCurrency(t *testing.T) { - t.Parallel() - i := Item{ - currency: currency.BTC, - } - if i.MatchesCurrency(currency.USDT) { - t.Error("expected false") - } - if !i.MatchesCurrency(currency.BTC) { - t.Error("expected true") - } - if i.MatchesCurrency(currency.EMPTYCODE) { - t.Error("expected false") - } - if i.MatchesCurrency(currency.NewCode("")) { - t.Error("expected false") - } -} - func TestCreateSnapshot(t *testing.T) { t.Parallel() f := FundManager{} @@ -928,7 +549,7 @@ func TestAddUSDTrackingData(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - quoteItem, err := CreateItem(exch, a, pair.Quote, elite, decimal.Zero) + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -951,7 +572,7 @@ func TestAddUSDTrackingData(t *testing.T) { dfk = &kline.DataFromKline{ Item: gctkline.Item{ - Exchange: exch, + Exchange: exchName, Pair: currency.NewPair(pair.Quote, currency.USD), Asset: a, Interval: gctkline.OneHour, @@ -971,7 +592,7 @@ func TestAddUSDTrackingData(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - usdtItem, err := CreateItem(exch, a, currency.USDT, elite, decimal.Zero) + usdtItem, err := CreateItem(exchName, a, currency.USDT, elite, decimal.Zero) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } @@ -997,18 +618,210 @@ func TestUSDTrackingDisabled(t *testing.T) { } } -func TestCanPlaceOrder(t *testing.T) { +func TestFundingLiquidate(t *testing.T) { + t.Parallel() + f := FundManager{} + f.Liquidate(nil) + f.items = append(f.items, &Item{ + exchange: "test", + asset: asset.Spot, + currency: currency.BTC, + available: decimal.NewFromInt(1337), + }) + + f.Liquidate(&signal.Signal{ + Base: event.Base{ + Exchange: "test", + AssetType: asset.Spot, + CurrencyPair: currency.NewPair(currency.BTC, currency.USD), + }, + }) + if !f.items[0].available.IsZero() { + t.Errorf("received '%v' expected '%v'", f.items[0].available, "0") + } +} + +func TestHasExchangeBeenLiquidated(t *testing.T) { + t.Parallel() + f := FundManager{} + f.Liquidate(nil) + f.items = append(f.items, &Item{ + exchange: "test", + asset: asset.Spot, + currency: currency.BTC, + available: decimal.NewFromInt(1337), + }) + ev := &signal.Signal{ + Base: event.Base{ + Exchange: "test", + AssetType: asset.Spot, + CurrencyPair: currency.NewPair(currency.BTC, currency.USD), + }, + } + f.Liquidate(ev) + if !f.items[0].available.IsZero() { + t.Errorf("received '%v' expected '%v'", f.items[0].available, "0") + } + if has := f.HasExchangeBeenLiquidated(ev); !has { + t.Errorf("received '%v' expected '%v'", has, true) + } +} + +func TestGetAllFunding(t *testing.T) { + t.Parallel() + f := FundManager{} + resp := f.GetAllFunding() + if len(resp) != 0 { + t.Errorf("received '%v' expected '%v'", len(resp), 0) + } + + f.items = append(f.items, &Item{ + exchange: "test", + asset: asset.Spot, + currency: currency.BTC, + available: decimal.NewFromInt(1337), + }) + + resp = f.GetAllFunding() + if len(resp) != 1 { + t.Errorf("received '%v' expected '%v'", len(resp), 1) + } +} + +func TestHasFutures(t *testing.T) { + t.Parallel() + f := FundManager{} + if has := f.HasFutures(); has { + t.Errorf("received '%v' expected '%v'", has, false) + } + + f.items = append(f.items, &Item{ + exchange: "test", + asset: asset.Futures, + currency: currency.BTC, + available: decimal.NewFromInt(1337), + }) + if has := f.HasFutures(); !has { + t.Errorf("received '%v' expected '%v'", has, true) + } +} + +func TestRealisePNL(t *testing.T) { t.Parallel() - item := &Item{} - c := &Collateral{ - contract: item, - collateral: item, + f := FundManager{} + f.items = append(f.items, &Item{ + exchange: "test", + asset: asset.Futures, + currency: currency.BTC, + available: decimal.NewFromInt(1336), + isCollateral: true, + }) + + var expectedError error + err := f.RealisePNL("test", asset.Futures, currency.BTC, decimal.NewFromInt(1)) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) } - if c.CanPlaceOrder(gctorder.Buy) { - t.Error("expected false") + if !f.items[0].available.Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", f.items[0].available, decimal.NewFromInt(1337)) } - c.collateral.available = decimal.NewFromInt(1337) - if !c.CanPlaceOrder(gctorder.Buy) { - t.Error("expected true") + + expectedError = ErrFundsNotFound + err = f.RealisePNL("test2", asset.Futures, currency.BTC, decimal.NewFromInt(1)) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCreateCollateral(t *testing.T) { + t.Parallel() + collat := &Item{ + exchange: "test", + asset: asset.Futures, + currency: currency.BTC, + available: decimal.NewFromInt(1336), + isCollateral: true, + } + contract := &Item{ + exchange: "test", + asset: asset.Futures, + currency: currency.DOGE, + available: decimal.NewFromInt(1336), + } + + var expectedError error + _, err := CreateCollateral(collat, contract) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + + expectedError = common.ErrNilArguments + _, err = CreateCollateral(nil, contract) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + + _, err = CreateCollateral(collat, nil) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestUpdateCollateral(t *testing.T) { + t.Parallel() + f := &FundManager{} + expectedError := common.ErrNilEvent + err := f.UpdateCollateral(nil) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + + ev := &signal.Signal{ + Base: event.Base{ + Exchange: "ftx", + AssetType: asset.Futures, + CurrencyPair: currency.NewPair(currency.BTC, currency.USD), + }, + } + f.items = append(f.items, &Item{ + exchange: "ftx", + asset: asset.Spot, + currency: currency.BTC, + available: decimal.NewFromInt(1336), + }) + em := engine.SetupExchangeManager() + exch, err := em.NewExchangeByName("ftx") + if err != nil { + t.Fatal(err) + } + exch.SetDefaults() + cfg, err := exch.GetDefaultConfig() + if err != nil { + t.Fatal(err) + } + err = exch.Setup(cfg) + if err != nil { + t.Fatal(err) + } + em.Add(exch) + f.exchangeManager = em + + expectedError = ErrFundsNotFound + err = f.UpdateCollateral(ev) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + + expectedError = nil + f.items = append(f.items, &Item{ + exchange: "ftx", + asset: asset.Futures, + currency: currency.USD, + available: decimal.NewFromInt(1336), + isCollateral: true, + }) + err = f.UpdateCollateral(ev) + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) } } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 40e6015e18a..4fa774d4241 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -138,15 +138,15 @@ type Item struct { collateralCandles map[currency.Code]kline.DataFromKline } -// Pair holds two currencies that are associated with each other -type Pair struct { +// SpotPair holds two currencies that are associated with each other +type SpotPair struct { base *Item quote *Item } -// Collateral consists of a currency pair for a futures contract +// CollateralPair consists of a currency pair for a futures contract // and associates it with an addition collateral pair to take funding from -type Collateral struct { +type CollateralPair struct { currentDirection *order.Side contract *Item collateral *Item diff --git a/backtester/funding/item.go b/backtester/funding/item.go index f4a58b03cfd..028dac834d2 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -95,19 +95,19 @@ func (i *Item) BasicEqual(exch string, a asset.Item, currency, pairedCurrency cu return i != nil && i.exchange == exch && i.asset == a && - i.currency == currency && + i.currency.Equal(currency) && (i.pairedWith == nil || (i.pairedWith != nil && i.pairedWith.currency.Equal(pairedCurrency))) } // MatchesCurrency checks that an item's currency is equal func (i *Item) MatchesCurrency(c currency.Code) bool { - return i != nil && i.currency == c + return i != nil && i.currency.Equal(c) } // MatchesItemCurrency checks that an item's currency is equal func (i *Item) MatchesItemCurrency(item *Item) bool { - return i != nil && item != nil && i.currency == item.currency + return i != nil && item != nil && i.currency.Equal(item.currency) } // MatchesExchange checks that an item's exchange is equal diff --git a/backtester/funding/item_test.go b/backtester/funding/item_test.go new file mode 100644 index 00000000000..5fa60479e2d --- /dev/null +++ b/backtester/funding/item_test.go @@ -0,0 +1,154 @@ +package funding + +import ( + "errors" + "testing" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" +) + +func TestMatchesExchange(t *testing.T) { + t.Parallel() + i := Item{} + if i.MatchesExchange(nil) { + t.Errorf("received '%v' expected '%v'", true, false) + } + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if !baseItem.MatchesExchange(quoteItem) { + t.Errorf("received '%v' expected '%v'", false, true) + } + if !baseItem.MatchesExchange(baseItem) { + t.Errorf("received '%v' expected '%v'", false, true) + } +} + +func TestMatchesItemCurrency(t *testing.T) { + t.Parallel() + i := Item{} + if i.MatchesItemCurrency(nil) { + t.Errorf("received '%v' expected '%v'", true, false) + } + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if baseItem.MatchesItemCurrency(quoteItem) { + t.Errorf("received '%v' expected '%v'", true, false) + } + if !baseItem.MatchesItemCurrency(baseItem) { + t.Errorf("received '%v' expected '%v'", false, true) + } +} + +func TestReserve(t *testing.T) { + t.Parallel() + i := Item{} + err := i.Reserve(decimal.Zero) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = i.Reserve(elite) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + + i.reserved = elite + err = i.Reserve(elite) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + + i.available = elite + err = i.Reserve(elite) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + err = i.Reserve(elite) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + + err = i.Reserve(neg) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } +} + +func TestIncreaseAvailable(t *testing.T) { + t.Parallel() + i := Item{} + i.IncreaseAvailable(elite) + if !i.available.Equal(elite) { + t.Errorf("expected %v", elite) + } + i.IncreaseAvailable(decimal.Zero) + i.IncreaseAvailable(neg) + if !i.available.Equal(elite) { + t.Errorf("expected %v", elite) + } +} + +func TestRelease(t *testing.T) { + t.Parallel() + i := Item{} + err := i.Release(decimal.Zero, decimal.Zero) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = i.Release(elite, decimal.Zero) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + i.reserved = elite + err = i.Release(elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + i.reserved = elite + err = i.Release(elite, one) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + err = i.Release(neg, decimal.Zero) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = i.Release(elite, neg) + if !errors.Is(err, errNegativeAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errNegativeAmountReceived) + } +} + +func TestMatchesCurrency(t *testing.T) { + t.Parallel() + i := Item{ + currency: currency.BTC, + } + if i.MatchesCurrency(currency.USDT) { + t.Error("expected false") + } + if !i.MatchesCurrency(currency.BTC) { + t.Error("expected true") + } + if i.MatchesCurrency(currency.EMPTYCODE) { + t.Error("expected false") + } + if i.MatchesCurrency(currency.NewCode("")) { + t.Error("expected false") + } +} diff --git a/backtester/funding/pair.go b/backtester/funding/spotpair.go similarity index 71% rename from backtester/funding/pair.go rename to backtester/funding/spotpair.go index e9d3e47bd91..1f1159df1c4 100644 --- a/backtester/funding/pair.go +++ b/backtester/funding/spotpair.go @@ -9,46 +9,38 @@ import ( ) var ( - // ErrNotPair is returned when a user requests funding pair details when it is a collateral pair - ErrNotPair = errors.New("not a funding pair") + // ErrNotCollateral is returned when a user requests collateral pair details when it is a funding pair + ErrNotCollateral = errors.New("not a collateral pair") ) // BaseInitialFunds returns the initial funds // from the base in a currency pair -func (p *Pair) BaseInitialFunds() decimal.Decimal { +func (p *SpotPair) BaseInitialFunds() decimal.Decimal { return p.base.initialFunds } // QuoteInitialFunds returns the initial funds // from the quote in a currency pair -func (p *Pair) QuoteInitialFunds() decimal.Decimal { +func (p *SpotPair) QuoteInitialFunds() decimal.Decimal { return p.quote.initialFunds } // BaseAvailable returns the available funds // from the base in a currency pair -func (p *Pair) BaseAvailable() decimal.Decimal { +func (p *SpotPair) BaseAvailable() decimal.Decimal { return p.base.available } // QuoteAvailable returns the available funds // from the quote in a currency pair -func (p *Pair) QuoteAvailable() decimal.Decimal { +func (p *SpotPair) QuoteAvailable() decimal.Decimal { return p.quote.available } -func (p *Pair) GetPairReader() (IPairReader, error) { - return p, nil -} - -func (p *Pair) GetCollateralReader() (ICollateralReader, error) { - return nil, ErrNotCollateral -} - // Reserve allocates an amount of funds to be used at a later time // it prevents multiple events from claiming the same resource // changes which currency to affect based on the order side -func (p *Pair) Reserve(amount decimal.Decimal, side order.Side) error { +func (p *SpotPair) Reserve(amount decimal.Decimal, side order.Side) error { switch side { case order.Buy: return p.quote.Reserve(amount) @@ -67,7 +59,7 @@ func (p *Pair) Reserve(amount decimal.Decimal, side order.Side) error { // Release reduces the amount of funding reserved and adds any difference // back to the available amount // changes which currency to affect based on the order side -func (p *Pair) Release(amount, diff decimal.Decimal, side order.Side) error { +func (p *SpotPair) Release(amount, diff decimal.Decimal, side order.Side) error { switch side { case order.Buy: return p.quote.Release(amount, diff) @@ -85,7 +77,7 @@ func (p *Pair) Release(amount, diff decimal.Decimal, side order.Side) error { // IncreaseAvailable adds funding to the available amount // changes which currency to affect based on the order side -func (p *Pair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { +func (p *SpotPair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { switch side { case order.Buy: p.base.IncreaseAvailable(amount) @@ -97,7 +89,7 @@ func (p *Pair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { // CanPlaceOrder does a > 0 check to see if there are any funds // to place an order with // changes which currency to affect based on the order side -func (p *Pair) CanPlaceOrder(side order.Side) bool { +func (p *SpotPair) CanPlaceOrder(side order.Side) bool { switch side { case order.Buy: return p.quote.CanPlaceOrder() @@ -107,18 +99,22 @@ func (p *Pair) CanPlaceOrder(side order.Side) bool { return false } -// FundReader returns a fund reader interface of the pair -func (p *Pair) FundReader() IFundReader { - return p +// Liquidate basic liquidation response to remove +// all asset value +func (p *SpotPair) Liquidate() { + p.base.available = decimal.Zero + p.base.reserved = decimal.Zero + p.quote.available = decimal.Zero + p.quote.reserved = decimal.Zero } // FundReserver returns a fund reserver interface of the pair -func (p *Pair) FundReserver() IFundReserver { +func (p *SpotPair) FundReserver() IFundReserver { return p } // PairReleaser returns a pair releaser interface of the pair -func (p *Pair) PairReleaser() (IPairReleaser, error) { +func (p *SpotPair) PairReleaser() (IPairReleaser, error) { if p == nil { return nil, ErrNilPair } @@ -126,20 +122,24 @@ func (p *Pair) PairReleaser() (IPairReleaser, error) { } // CollateralReleaser returns an error because a pair is not collateral -func (p *Pair) CollateralReleaser() (ICollateralReleaser, error) { +func (p *SpotPair) CollateralReleaser() (ICollateralReleaser, error) { return nil, ErrNotCollateral } // FundReleaser returns a pair releaser interface of the pair -func (p *Pair) FundReleaser() IFundReleaser { +func (p *SpotPair) FundReleaser() IFundReleaser { return p } -// Liquidate basic liquidation response to remove -// all asset value -func (p *Pair) Liquidate() { - p.base.available = decimal.Zero - p.base.reserved = decimal.Zero - p.quote.available = decimal.Zero - p.quote.reserved = decimal.Zero +// FundReader returns a fund reader interface of the pair +func (p *SpotPair) FundReader() IFundReader { + return p +} + +func (p *SpotPair) GetPairReader() (IPairReader, error) { + return p, nil +} + +func (p *SpotPair) GetCollateralReader() (ICollateralReader, error) { + return nil, ErrNotCollateral } diff --git a/backtester/funding/spotpair_test.go b/backtester/funding/spotpair_test.go new file mode 100644 index 00000000000..3c8ae5a141c --- /dev/null +++ b/backtester/funding/spotpair_test.go @@ -0,0 +1,345 @@ +package funding + +import ( + "errors" + "testing" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +func TestBaseInitialFunds(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + funds := pairItems.BaseInitialFunds() + if !funds.IsZero() { + t.Errorf("received '%v' expected '%v'", funds, baseItem.available) + } +} + +func TestQuoteInitialFunds(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + funds := pairItems.QuoteInitialFunds() + if !funds.Equal(elite) { + t.Errorf("received '%v' expected '%v'", funds, elite) + } +} + +func TestBaseAvailable(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + funds := pairItems.BaseAvailable() + if !funds.IsZero() { + t.Errorf("received '%v' expected '%v'", funds, baseItem.available) + } +} + +func TestQuoteAvailable(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + funds := pairItems.QuoteAvailable() + if !funds.Equal(elite) { + t.Errorf("received '%v' expected '%v'", funds, elite) + } +} + +func TestReservePair(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + err = pairItems.Reserve(decimal.Zero, gctorder.Buy) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = pairItems.Reserve(elite, gctorder.Buy) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = pairItems.Reserve(decimal.Zero, gctorder.Sell) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = pairItems.Reserve(elite, gctorder.Sell) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + err = pairItems.Reserve(elite, common.DoNothing) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } +} + +func TestReleasePair(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + err = pairItems.Reserve(decimal.Zero, gctorder.Buy) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = pairItems.Reserve(elite, gctorder.Buy) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = pairItems.Reserve(decimal.Zero, gctorder.Sell) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = pairItems.Reserve(elite, gctorder.Sell) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + + err = pairItems.Release(decimal.Zero, decimal.Zero, gctorder.Buy) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = pairItems.Release(elite, decimal.Zero, gctorder.Buy) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = pairItems.Release(elite, decimal.Zero, gctorder.Buy) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + + err = pairItems.Release(elite, decimal.Zero, common.DoNothing) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + + err = pairItems.Release(elite, decimal.Zero, gctorder.Sell) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } + err = pairItems.Release(decimal.Zero, decimal.Zero, gctorder.Sell) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } +} + +func TestIncreaseAvailablePair(t *testing.T) { + t.Parallel() + baseItem, err := CreateItem(exchName, a, pair.Base, decimal.Zero, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + quoteItem, err := CreateItem(exchName, a, pair.Quote, elite, decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + baseItem.pairedWith = quoteItem + quoteItem.pairedWith = baseItem + pairItems := SpotPair{base: baseItem, quote: quoteItem} + pairItems.IncreaseAvailable(decimal.Zero, gctorder.Buy) + if !pairItems.quote.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) + } + pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) + if !pairItems.base.available.IsZero() { + t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.base.available) + } + + pairItems.IncreaseAvailable(elite.Neg(), gctorder.Sell) + if !pairItems.quote.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) + } + pairItems.IncreaseAvailable(elite, gctorder.Buy) + if !pairItems.base.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) + } + + pairItems.IncreaseAvailable(elite, common.DoNothing) + if !pairItems.base.available.Equal(elite) { + t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) + } +} + +func TestCanPlaceOrderPair(t *testing.T) { + t.Parallel() + p := SpotPair{ + base: &Item{}, + quote: &Item{}, + } + if p.CanPlaceOrder(common.DoNothing) { + t.Error("expected false") + } + if p.CanPlaceOrder(gctorder.Buy) { + t.Error("expected false") + } + if p.CanPlaceOrder(gctorder.Sell) { + t.Error("expected false") + } + + p.quote.available = decimal.NewFromInt(32) + if !p.CanPlaceOrder(gctorder.Buy) { + t.Error("expected true") + } + p.base.available = decimal.NewFromInt(32) + if !p.CanPlaceOrder(gctorder.Sell) { + t.Error("expected true") + } +} + +func TestGetPairReader(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + var expectedError error + ip, err := p.GetPairReader() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } + if ip != p { + t.Error("expected the same thing") + } +} + +func TestGetCollateralReader(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + expectedError := ErrNotCollateral + _, err := p.GetCollateralReader() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestFundReader(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + ip := p.FundReader() + if ip != p { + t.Error("expected the same thing") + } +} + +func TestFundReserver(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + ip := p.FundReserver() + if ip != p { + t.Error("expected the same thing") + } +} + +func TestFundReleaser(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + ip := p.FundReleaser() + if ip != p { + t.Error("expected the same thing") + } +} + +func TestPairReleaser(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + var expectedError error + _, err := p.PairReleaser() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestCollateralReleaser(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{exchange: "hello"}, + } + expectedError := ErrNotCollateral + _, err := p.CollateralReleaser() + if !errors.Is(err, expectedError) { + t.Errorf("recevied '%v' expected '%v'", err, expectedError) + } +} + +func TestLiquidate(t *testing.T) { + t.Parallel() + p := &SpotPair{ + base: &Item{ + available: decimal.NewFromInt(1337), + }, + quote: &Item{ + available: decimal.NewFromInt(1337), + }, + } + p.Liquidate() + if !p.base.available.IsZero() { + t.Errorf("received '%v' expected '%v'", p.base.available, "0") + } + if !p.quote.available.IsZero() { + t.Errorf("received '%v' expected '%v'", p.quote.available, "0") + } +} diff --git a/backtester/report/chart.go b/backtester/report/chart.go index 8474733aca9..ab63dcda35d 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -6,20 +6,21 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) // createUSDTotalsChart used for creating a chart in the HTML report // to show how much the overall assets are worth over time -func (d *Data) createUSDTotalsChart() *Chart { - if d.Statistics.FundingStatistics == nil || d.Statistics.FundingStatistics.Report.DisableUSDTracking { +func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.FundingItemStatistics) *Chart { + if items == nil || stats == nil { return nil } response := &Chart{} var usdTotalChartPlot []LinePlot - for i := range d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues { + for i := range items { usdTotalChartPlot = append(usdTotalChartPlot, LinePlot{ - Value: d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues[i].Value.InexactFloat64(), - UnixMilli: d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues[i].Time.UTC().UnixMilli(), + Value: items[i].Value.InexactFloat64(), + UnixMilli: items[i].Time.UTC().UnixMilli(), }) } response.Data = append(response.Data, ChartLine{ @@ -27,16 +28,16 @@ func (d *Data) createUSDTotalsChart() *Chart { LinePlots: usdTotalChartPlot, }) - for i := range d.Statistics.FundingStatistics.Items { + for i := range stats { var plots []LinePlot - for j := range d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots { + for j := range stats[i].ReportItem.Snapshots { plots = append(plots, LinePlot{ - Value: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].USDValue.InexactFloat64(), - UnixMilli: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), + Value: stats[i].ReportItem.Snapshots[j].USDValue.InexactFloat64(), + UnixMilli: stats[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), }) } response.Data = append(response.Data, ChartLine{ - Name: fmt.Sprintf("%v %v %v USD value", d.Statistics.FundingStatistics.Items[i].ReportItem.Exchange, d.Statistics.FundingStatistics.Items[i].ReportItem.Asset, d.Statistics.FundingStatistics.Items[i].ReportItem.Currency), + Name: fmt.Sprintf("%v %v %v USD value", stats[i].ReportItem.Exchange, stats[i].ReportItem.Asset, stats[i].ReportItem.Currency), LinePlots: plots, }) } @@ -44,9 +45,9 @@ func (d *Data) createUSDTotalsChart() *Chart { return response } -func (d *Data) createPNLCharts() *Chart { +func createPNLCharts(items map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) *Chart { response := &Chart{} - for exch, assetMap := range d.Statistics.ExchangeAssetPairStatistics { + for exch, assetMap := range items { for item, pairMap := range assetMap { for pair, result := range pairMap { id := fmt.Sprintf("%v %v %v", @@ -83,26 +84,27 @@ func (d *Data) createPNLCharts() *Chart { // createHoldingsOverTimeChart used for creating a chart in the HTML report // to show how many holdings of each type was held over the time of backtesting -func (d *Data) createHoldingsOverTimeChart() *Chart { - if d.Statistics.FundingStatistics == nil { +func createHoldingsOverTimeChart(items []statistics.FundingItemStatistics) *Chart { + if items == nil { return nil } - response := &Chart{} - response.AxisType = "logarithmic" - for i := range d.Statistics.FundingStatistics.Items { + response := &Chart{ + AxisType: "logarithmic", + } + for i := range items { var plots []LinePlot - for j := range d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots { - if d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Available.IsZero() { + for j := range items[i].ReportItem.Snapshots { + if items[i].ReportItem.Snapshots[j].Available.IsZero() { // highcharts can't render zeroes in logarithmic mode response.AxisType = "linear" } plots = append(plots, LinePlot{ - Value: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Available.InexactFloat64(), - UnixMilli: d.Statistics.FundingStatistics.Items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), + Value: items[i].ReportItem.Snapshots[j].Available.InexactFloat64(), + UnixMilli: items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), }) } response.Data = append(response.Data, ChartLine{ - Name: fmt.Sprintf("%v %v %v holdings", d.Statistics.FundingStatistics.Items[i].ReportItem.Exchange, d.Statistics.FundingStatistics.Items[i].ReportItem.Asset, d.Statistics.FundingStatistics.Items[i].ReportItem.Currency), + Name: fmt.Sprintf("%v %v %v holdings", items[i].ReportItem.Exchange, items[i].ReportItem.Asset, items[i].ReportItem.Currency), LinePlots: plots, }) } @@ -110,18 +112,10 @@ func (d *Data) createHoldingsOverTimeChart() *Chart { return response } -type linkCurrencyDiff struct { - FuturesPair currency.Pair - SpotPair currency.Pair - FuturesEvents []statistics.DataAtOffset - SpotEvents []statistics.DataAtOffset - DiffPercent []decimal.Decimal -} - -func (d *Data) createFuturesSpotDiffChart() *Chart { +func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) *Chart { var currs []linkCurrencyDiff response := &Chart{} - for _, assetMap := range d.Statistics.ExchangeAssetPairStatistics { + for _, assetMap := range items { for item, pairMap := range assetMap { if !item.IsFutures() { continue @@ -135,7 +129,7 @@ func (d *Data) createFuturesSpotDiffChart() *Chart { } } } - for _, assetMap := range d.Statistics.ExchangeAssetPairStatistics { + for _, assetMap := range items { for item, pairMap := range assetMap { if item.IsFutures() { continue diff --git a/backtester/report/chart_test.go b/backtester/report/chart_test.go new file mode 100644 index 00000000000..61545b0e042 --- /dev/null +++ b/backtester/report/chart_test.go @@ -0,0 +1,83 @@ +package report + +import ( + "testing" + "time" + + "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" + "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +func TestCreateUSDTotalsChart(t *testing.T) { + t.Parallel() + if resp := createUSDTotalsChart(nil, nil); resp != nil { + t.Error("expected nil") + } + tt := time.Now() + items := []statistics.ValueAtTime{ + { + Time: tt, + Value: decimal.NewFromInt(1337), + Set: true, + }, + } + if resp := createUSDTotalsChart(items, nil); resp != nil { + t.Error("expected nil") + } + stats := []statistics.FundingItemStatistics{ + { + ReportItem: &funding.ReportItem{ + Snapshots: []funding.ItemSnapshot{ + { + Time: tt, + USDValue: decimal.NewFromInt(1337), + }, + }, + }, + }, + } + resp := createUSDTotalsChart(items, stats) + if resp == nil { + t.Error("expected not nil") + } + if resp.Data[0].Name != "Total USD value" { + t.Error("expected not nil") + } + if resp.Data[0].LinePlots[0].Value != 1337 { + t.Error("expected not nil") + } +} + +func TestCreateHoldingsOverTimeChart(t *testing.T) { + t.Parallel() + if resp := createHoldingsOverTimeChart(nil); resp != nil { + t.Fatal("expected nil") + } + tt := time.Now() + items := []statistics.FundingItemStatistics{ + { + ReportItem: &funding.ReportItem{ + Exchange: "hello", + Asset: asset.Spot, + Currency: currency.BTC, + Snapshots: []funding.ItemSnapshot{ + { + Time: tt, + Available: decimal.NewFromInt(1337), + }, + { + Time: tt, + Available: decimal.Zero, + }, + }, + }, + }, + } + resp := createHoldingsOverTimeChart(items) + if resp.AxisType != "linear" { + t.Error("expected linear from zero available") + } +} diff --git a/backtester/report/report.go b/backtester/report/report.go index 70e9e2e9da9..397bb899ab5 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -42,10 +42,12 @@ func (d *Data) GenerateReport() error { d.EnhancedCandles[i].Candles = d.EnhancedCandles[i].Candles[:maxChartLimit] } } - d.USDTotalsChart = d.createUSDTotalsChart() - d.HoldingsOverTimeChart = d.createHoldingsOverTimeChart() - d.PNLOverTimeChart = d.createPNLCharts() - d.FuturesSpotDiffChart = d.createFuturesSpotDiffChart() + if d.Statistics.FundingStatistics != nil && !d.Statistics.FundingStatistics.Report.DisableUSDTracking { + d.USDTotalsChart = createUSDTotalsChart(d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues, d.Statistics.FundingStatistics.Items) + d.HoldingsOverTimeChart = createHoldingsOverTimeChart(d.Statistics.FundingStatistics.Items) + } + d.PNLOverTimeChart = createPNLCharts(d.Statistics.ExchangeAssetPairStatistics) + d.FuturesSpotDiffChart = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) tmpl := template.Must( template.ParseFiles( diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index ea7d30b61cf..b2d25cbe349 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -105,6 +105,14 @@ type DetailedCandle struct { PurchasePrice float64 } +type linkCurrencyDiff struct { + FuturesPair currency.Pair + SpotPair currency.Pair + FuturesEvents []statistics.DataAtOffset + SpotEvents []statistics.DataAtOffset + DiffPercent []decimal.Decimal +} + // PrettyNumbers is used for report rendering // one cannot access packages when rendering data in a template // this struct exists purely to help make numbers look pretty From 13280b05947ec1253511c92b4c7bc1e0e5173da6 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 12:09:52 +1000 Subject: [PATCH 123/171] Completes coverage of report package --- backtester/report/chart.go | 86 ++++++++++------- backtester/report/chart_test.go | 154 ++++++++++++++++++++++++++++-- backtester/report/report.go | 38 +++++--- backtester/report/report_test.go | 88 ++++++++++++++--- backtester/report/report_types.go | 6 +- 5 files changed, 302 insertions(+), 70 deletions(-) diff --git a/backtester/report/chart.go b/backtester/report/chart.go index ab63dcda35d..df38d7ca503 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -11,9 +12,12 @@ import ( // createUSDTotalsChart used for creating a chart in the HTML report // to show how much the overall assets are worth over time -func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.FundingItemStatistics) *Chart { - if items == nil || stats == nil { - return nil +func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.FundingItemStatistics) (*Chart, error) { + if items == nil { + return nil, fmt.Errorf("%w missing values at time", common.ErrNilArguments) + } + if stats == nil { + return nil, fmt.Errorf("%w missing funding item statistics", common.ErrNilArguments) } response := &Chart{} var usdTotalChartPlot []LinePlot @@ -42,10 +46,45 @@ func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.Fun }) } - return response + return response, nil +} + +// createHoldingsOverTimeChart used for creating a chart in the HTML report +// to show how many holdings of each type was held over the time of backtesting +func createHoldingsOverTimeChart(items []statistics.FundingItemStatistics) (*Chart, error) { + if items == nil { + return nil, fmt.Errorf("%w missing funding item statistics", common.ErrNilArguments) + } + response := &Chart{ + AxisType: "logarithmic", + } + for i := range items { + var plots []LinePlot + for j := range items[i].ReportItem.Snapshots { + if items[i].ReportItem.Snapshots[j].Available.IsZero() { + // highcharts can't render zeroes in logarithmic mode + response.AxisType = "linear" + } + plots = append(plots, LinePlot{ + Value: items[i].ReportItem.Snapshots[j].Available.InexactFloat64(), + UnixMilli: items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), + }) + } + response.Data = append(response.Data, ChartLine{ + Name: fmt.Sprintf("%v %v %v holdings", items[i].ReportItem.Exchange, items[i].ReportItem.Asset, items[i].ReportItem.Currency), + LinePlots: plots, + }) + } + + return response, nil } -func createPNLCharts(items map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) *Chart { +// createPNLCharts shows a running history of all realised and unrealised PNL values +// over time +func createPNLCharts(items map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) (*Chart, error) { + if items == nil { + return nil, fmt.Errorf("%w missing currency pair statistics", common.ErrNilArguments) + } response := &Chart{} for exch, assetMap := range items { for item, pairMap := range assetMap { @@ -79,40 +118,15 @@ func createPNLCharts(items map[string]map[asset.Item]map[currency.Pair]*statisti } } - return response + return response, nil } -// createHoldingsOverTimeChart used for creating a chart in the HTML report -// to show how many holdings of each type was held over the time of backtesting -func createHoldingsOverTimeChart(items []statistics.FundingItemStatistics) *Chart { +// createFuturesSpotDiffChart highlights the difference in futures and spot prices +// over time +func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) (*Chart, error) { if items == nil { - return nil - } - response := &Chart{ - AxisType: "logarithmic", - } - for i := range items { - var plots []LinePlot - for j := range items[i].ReportItem.Snapshots { - if items[i].ReportItem.Snapshots[j].Available.IsZero() { - // highcharts can't render zeroes in logarithmic mode - response.AxisType = "linear" - } - plots = append(plots, LinePlot{ - Value: items[i].ReportItem.Snapshots[j].Available.InexactFloat64(), - UnixMilli: items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), - }) - } - response.Data = append(response.Data, ChartLine{ - Name: fmt.Sprintf("%v %v %v holdings", items[i].ReportItem.Exchange, items[i].ReportItem.Asset, items[i].ReportItem.Currency), - LinePlots: plots, - }) + return nil, fmt.Errorf("%w missing currency pair statistics", common.ErrNilArguments) } - - return response -} - -func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) *Chart { var currs []linkCurrencyDiff response := &Chart{} for _, assetMap := range items { @@ -164,5 +178,5 @@ func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pai } response.Data = append(response.Data, line) } - return response + return response, nil } diff --git a/backtester/report/chart_test.go b/backtester/report/chart_test.go index 61545b0e042..19e1afc0fdd 100644 --- a/backtester/report/chart_test.go +++ b/backtester/report/chart_test.go @@ -1,20 +1,27 @@ package report import ( + "errors" "testing" "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" + evkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) func TestCreateUSDTotalsChart(t *testing.T) { t.Parallel() - if resp := createUSDTotalsChart(nil, nil); resp != nil { - t.Error("expected nil") + _, err := createUSDTotalsChart(nil, nil) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilArguments) } tt := time.Now() items := []statistics.ValueAtTime{ @@ -24,8 +31,9 @@ func TestCreateUSDTotalsChart(t *testing.T) { Set: true, }, } - if resp := createUSDTotalsChart(items, nil); resp != nil { - t.Error("expected nil") + _, err = createUSDTotalsChart(items, nil) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilArguments) } stats := []statistics.FundingItemStatistics{ { @@ -39,7 +47,10 @@ func TestCreateUSDTotalsChart(t *testing.T) { }, }, } - resp := createUSDTotalsChart(items, stats) + resp, err := createUSDTotalsChart(items, stats) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if resp == nil { t.Error("expected not nil") } @@ -53,8 +64,9 @@ func TestCreateUSDTotalsChart(t *testing.T) { func TestCreateHoldingsOverTimeChart(t *testing.T) { t.Parallel() - if resp := createHoldingsOverTimeChart(nil); resp != nil { - t.Fatal("expected nil") + _, err := createHoldingsOverTimeChart(nil) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilArguments) } tt := time.Now() items := []statistics.FundingItemStatistics{ @@ -76,8 +88,134 @@ func TestCreateHoldingsOverTimeChart(t *testing.T) { }, }, } - resp := createHoldingsOverTimeChart(items) + resp, err := createHoldingsOverTimeChart(items) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if resp.AxisType != "linear" { t.Error("expected linear from zero available") } } + +func TestCreatePNLCharts(t *testing.T) { + t.Parallel() + _, err := createPNLCharts(nil) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilArguments) + } + + tt := time.Now() + var d Data + d.Statistics = &statistics.Statistic{} + d.Statistics.ExchangeAssetPairStatistics = make(map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange] = make(map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange][asset.Spot] = make(map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange][asset.Spot][currency.NewPair(currency.BTC, currency.USDT)] = &statistics.CurrencyPairStatistic{ + Events: []statistics.DataAtOffset{ + { + PNL: &portfolio.PNLSummary{ + Result: gctorder.PNLResult{ + Time: tt, + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1337), + RealisedPNL: decimal.NewFromInt(1337), + Price: decimal.NewFromInt(1337), + Exposure: decimal.NewFromInt(1337), + Direction: gctorder.Short, + }, + }, + }, + }, + } + + d.AddKlineItem(&gctkline.Item{ + Exchange: testExchange, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Asset: asset.Spot, + Interval: gctkline.OneDay, + Candles: []gctkline.Candle{ + { + Time: tt, + Open: 1336, + High: 1338, + Low: 1336, + Close: 1337, + Volume: 1337, + }, + }, + }) + err = d.enhanceCandles() + if err != nil { + t.Error(err) + } + + _, err = createPNLCharts(d.Statistics.ExchangeAssetPairStatistics) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } +} + +func TestCreateFuturesSpotDiffChart(t *testing.T) { + t.Parallel() + _, err := createFuturesSpotDiffChart(nil) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilArguments) + } + + tt := time.Now() + cp := currency.NewPair(currency.BTC, currency.USD) + cp2 := currency.NewPair(currency.BTC, currency.DOGE) + var d Data + d.Statistics = &statistics.Statistic{} + d.Statistics.ExchangeAssetPairStatistics = make(map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange] = make(map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange][asset.Spot] = make(map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange][asset.Spot][cp] = &statistics.CurrencyPairStatistic{ + Currency: cp, + Events: []statistics.DataAtOffset{ + { + Time: tt, + DataEvent: &evkline.Kline{Close: decimal.NewFromInt(1337)}, + PNL: &portfolio.PNLSummary{ + Result: gctorder.PNLResult{ + Time: tt, + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1337), + RealisedPNL: decimal.NewFromInt(1337), + Price: decimal.NewFromInt(1337), + Exposure: decimal.NewFromInt(1337), + Direction: gctorder.Buy, + }, + }, + }, + }, + } + d.Statistics.ExchangeAssetPairStatistics[testExchange][asset.Futures] = make(map[currency.Pair]*statistics.CurrencyPairStatistic) + d.Statistics.ExchangeAssetPairStatistics[testExchange][asset.Futures][cp2] = &statistics.CurrencyPairStatistic{ + UnderlyingPair: cp, + Currency: cp2, + Events: []statistics.DataAtOffset{ + { + Time: tt, + DataEvent: &evkline.Kline{Close: decimal.NewFromInt(1337)}, + PNL: &portfolio.PNLSummary{ + Result: gctorder.PNLResult{ + Time: tt, + UnrealisedPNL: decimal.NewFromInt(1337), + RealisedPNLBeforeFees: decimal.NewFromInt(1337), + RealisedPNL: decimal.NewFromInt(1337), + Price: decimal.NewFromInt(1337), + Exposure: decimal.NewFromInt(1337), + Direction: gctorder.Short, + }, + }, + }, + }, + } + + _, err = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } +} diff --git a/backtester/report/report.go b/backtester/report/report.go index 397bb899ab5..dda64b7746d 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -42,12 +42,28 @@ func (d *Data) GenerateReport() error { d.EnhancedCandles[i].Candles = d.EnhancedCandles[i].Candles[:maxChartLimit] } } - if d.Statistics.FundingStatistics != nil && !d.Statistics.FundingStatistics.Report.DisableUSDTracking { - d.USDTotalsChart = createUSDTotalsChart(d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues, d.Statistics.FundingStatistics.Items) - d.HoldingsOverTimeChart = createHoldingsOverTimeChart(d.Statistics.FundingStatistics.Items) + + if d.Statistics.FundingStatistics != nil { + d.HoldingsOverTimeChart, err = createHoldingsOverTimeChart(d.Statistics.FundingStatistics.Items) + if err != nil { + return err + } + if !d.Statistics.FundingStatistics.Report.DisableUSDTracking { + d.USDTotalsChart, err = createUSDTotalsChart(d.Statistics.FundingStatistics.TotalUSDStatistics.HoldingValues, d.Statistics.FundingStatistics.Items) + if err != nil { + return err + } + } + } + + d.PNLOverTimeChart, err = createPNLCharts(d.Statistics.ExchangeAssetPairStatistics) + if err != nil { + return err + } + d.FuturesSpotDiffChart, err = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) + if err != nil { + return err } - d.PNLOverTimeChart = createPNLCharts(d.Statistics.ExchangeAssetPairStatistics) - d.FuturesSpotDiffChart = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) tmpl := template.Must( template.ParseFiles( @@ -116,7 +132,7 @@ func (d *Data) enhanceCandles() error { for intVal := range d.OriginalCandles { lookup := d.OriginalCandles[intVal] - enhancedKline := DetailedKline{ + enhancedKline := EnhancedKline{ Exchange: lookup.Exchange, Asset: lookup.Asset, Pair: lookup.Pair, @@ -196,12 +212,12 @@ func (d *Data) enhanceCandles() error { return nil } -func (d *DetailedCandle) copyCloseFromPreviousEvent(enhancedKline *DetailedKline) { +func (d *DetailedCandle) copyCloseFromPreviousEvent(ek *EnhancedKline) { // if the data is missing, ensure that all values just continue the previous candle's close price visually - d.Open = enhancedKline.Candles[len(enhancedKline.Candles)-1].Close - d.High = enhancedKline.Candles[len(enhancedKline.Candles)-1].Close - d.Low = enhancedKline.Candles[len(enhancedKline.Candles)-1].Close - d.Close = enhancedKline.Candles[len(enhancedKline.Candles)-1].Close + d.Open = ek.Candles[len(ek.Candles)-1].Close + d.High = ek.Candles[len(ek.Candles)-1].Close + d.Low = ek.Candles[len(ek.Candles)-1].Close + d.Close = ek.Candles[len(ek.Candles)-1].Close d.Colour = "white" d.Position = "aboveBar" d.Shape = "arrowDown" diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index b39b7e67728..5c92d60d257 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -11,7 +11,6 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/config" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" @@ -56,7 +55,7 @@ func TestGenerateReport(t *testing.T) { }, }, }, - EnhancedCandles: []DetailedKline{ + EnhancedCandles: []EnhancedKline{ { Exchange: e, Asset: a, @@ -231,13 +230,23 @@ func TestGenerateReport(t *testing.T) { }, }, Statistics: &statistics.Statistic{ + FundingStatistics: &statistics.FundingStatistics{ + Report: &funding.Report{ + DisableUSDTracking: true, + }, + Items: []statistics.FundingItemStatistics{ + { + ReportItem: &funding.ReportItem{Snapshots: []funding.ItemSnapshot{{Time: time.Now()}}}, + }, + }, + TotalUSDStatistics: &statistics.TotalFundingStatistics{}, + }, StrategyName: "testStrat", RiskFreeRate: decimal.NewFromFloat(0.03), ExchangeAssetPairStatistics: map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic{ e: { a: { p: &statistics.CurrencyPairStatistic{ - MaxDrawdown: statistics.Swing{}, LowestClosePrice: statistics.ValueAtTime{Value: decimal.NewFromInt(100)}, HighestClosePrice: statistics.ValueAtTime{Value: decimal.NewFromInt(200)}, MarketMovement: decimal.NewFromInt(100), @@ -245,8 +254,6 @@ func TestGenerateReport(t *testing.T) { CompoundAnnualGrowthRate: decimal.NewFromInt(1), BuyOrders: 1, SellOrders: 1, - FinalHoldings: holdings.Holding{}, - FinalOrders: compliance.Snapshot{}, ArithmeticRatios: &statistics.Ratios{}, GeometricRatios: &statistics.Ratios{}, }, @@ -310,17 +317,10 @@ func TestGenerateReport(t *testing.T) { MarketMovement: decimal.NewFromInt(1337), StrategyMovement: decimal.NewFromInt(1337), }, - WasAnyDataMissing: false, - FundingStatistics: nil, }, } d.OutputPath = tempDir d.Config.StrategySettings.DisableUSDTracking = true - d.Statistics.FundingStatistics = &statistics.FundingStatistics{ - Report: &funding.Report{ - DisableUSDTracking: true, - }, - } err = d.GenerateReport() if err != nil { t.Error(err) @@ -463,3 +463,67 @@ func TestEnhanceCandles(t *testing.T) { t.Error("expected enhanced candles") } } + +func TestUpdateItem(t *testing.T) { + t.Parallel() + d := Data{} + tt := time.Now() + d.UpdateItem(&gctkline.Item{ + Candles: []gctkline.Candle{ + { + Time: tt, + }, + }, + }) + if len(d.OriginalCandles) != 1 { + t.Fatal("expected Original Candles len of 1") + } + if len(d.OriginalCandles[0].Candles) != 1 { + t.Error("expected one candle") + } + d.UpdateItem(&gctkline.Item{ + Candles: []gctkline.Candle{ + { + Time: tt, + }, + }, + }) + if len(d.OriginalCandles[0].Candles) != 1 { + t.Error("expected one candle") + } + + d.UpdateItem(&gctkline.Item{ + Candles: []gctkline.Candle{ + { + Time: tt.Add(1), + }, + }, + }) + if len(d.OriginalCandles[0].Candles) != 2 { + t.Error("expected two candles") + } +} + +func TestCopyCloseFromPreviousEvent(t *testing.T) { + t.Parallel() + d := DetailedCandle{} + d.copyCloseFromPreviousEvent(&EnhancedKline{ + Candles: []DetailedCandle{ + { + Close: 1337, + }, + }, + }) + if d.Close != 1337 { + t.Error("expected 1337") + } +} + +func TestUseDarkMode(t *testing.T) { + t.Parallel() + d := Data{} + d.UseDarkMode(true) + if !d.UseDarkTheme { + t.Error("expected true") + } +} diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index b2d25cbe349..82c55094539 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -32,7 +32,7 @@ type Handler interface { // Data holds all statistical information required to output detailed backtesting results type Data struct { OriginalCandles []*kline.Item - EnhancedCandles []DetailedKline + EnhancedCandles []EnhancedKline Statistics *statistics.Statistic Config *config.Config TemplatePath string @@ -75,8 +75,8 @@ type Warning struct { Message string } -// DetailedKline enhances kline details for the purpose of rich reporting results -type DetailedKline struct { +// EnhancedKline enhances kline details for the purpose of rich reporting results +type EnhancedKline struct { IsOverLimit bool Watermark string Exchange string From ae310e2aa08aaee78435241bbda79d40ddb80429 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 14:13:05 +1000 Subject: [PATCH 124/171] Documentation, fixes some assumptions on asset errors --- README.md | 10 +-- backtester/README.md | 6 +- backtester/config/README.md | 33 +++++++-- backtester/config/examples/README.md | 1 + backtester/engine/backtest.go | 10 ++- .../strategies/ftxcashandcarry/README.md | 68 +++++++++++++++++++ backtester/funding/README.md | 8 +++ backtester/funding/item.go | 11 +-- .../backtester_config_examples_readme.tmpl | 1 + .../backtester_config_readme.tmpl | 33 +++++++-- ...ers_strategies_ftxcashandcarry_readme.tmpl | 34 ++++++++++ .../backtester_funding_readme.tmpl | 8 +++ .../backtester_readme.tmpl | 6 +- 13 files changed, 195 insertions(+), 34 deletions(-) create mode 100644 backtester/eventhandlers/strategies/ftxcashandcarry/README.md create mode 100644 cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl diff --git a/README.md b/README.md index 42c441cfed4..32305c68472 100644 --- a/README.md +++ b/README.md @@ -143,11 +143,11 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| -| [thrasher-](https://github.com/thrasher-) | 664 | -| [shazbert](https://github.com/shazbert) | 232 | -| [gloriousCode](https://github.com/gloriousCode) | 194 | +| [thrasher-](https://github.com/thrasher-) | 665 | +| [shazbert](https://github.com/shazbert) | 240 | +| [gloriousCode](https://github.com/gloriousCode) | 195 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | -| [dependabot[bot]](https://github.com/apps/dependabot) | 57 | +| [dependabot[bot]](https://github.com/apps/dependabot) | 68 | | [xtda](https://github.com/xtda) | 47 | | [lrascao](https://github.com/lrascao) | 27 | | [Rots](https://github.com/Rots) | 15 | @@ -160,7 +160,7 @@ Binaries will be published once the codebase reaches a stable condition. | [marcofranssen](https://github.com/marcofranssen) | 8 | | [dackroyd](https://github.com/dackroyd) | 5 | | [cranktakular](https://github.com/cranktakular) | 5 | -| [khcchiu](https://github.com/khcchiu) | 4 | +| [khcchiu](https://github.com/khcchiu) | 5 | | [woshidama323](https://github.com/woshidama323) | 3 | | [yangrq1018](https://github.com/yangrq1018) | 3 | | [TaltaM](https://github.com/TaltaM) | 3 | diff --git a/backtester/README.md b/backtester/README.md index a428001a5e9..a46bf5e7b4a 100644 --- a/backtester/README.md +++ b/backtester/README.md @@ -43,14 +43,16 @@ An event-driven backtesting tool to test and iterate trading strategies using hi - Compliance manager to keep snapshots of every transaction and their changes at every interval - Exchange level funding allows funding to be shared across multiple currency pairs and to allow for complex strategy design - Fund transfer. At a strategy level, transfer funds between exchanges to allow for complex strategy design +- Backtesting support for futures asset types +- Example cash and carry spot futures strategy ## Planned Features We welcome pull requests on any feature for the Backtester! We will be especially appreciative of any contribution towards the following planned features: | Feature | Description | |---------|-------------| -| Add backtesting support for futures asset types | Spot trading is currently the only supported asset type. Futures trading greatly expands the Backtester's potential | -| Example futures pairs trading strategy | Providing a basic example will allow for esteemed traders to build and customise their own | +| Long-running application | Transform the Backtester to run a GRPC server, where commands can be sent to run Backtesting operations. Allowing for many strategies to be run, analysed and tweaked in a more efficient manner | +| Enhance config-builder | Create an application that can create strategy configs in a more visual manner and execute them via GRPC to allow for faster customisation of strategies | | Save Backtester results to database | This will allow for easier comparison of results over time | | Backtester result comparison report | Providing an executive summary of Backtester database results | | Currency correlation | Compare multiple exchange, asset, currencies for a candle interval against indicators to highlight correlated pairs for use in pairs trading | diff --git a/backtester/config/README.md b/backtester/config/README.md index 4e8f75b1f29..025f6b95b7a 100644 --- a/backtester/config/README.md +++ b/backtester/config/README.md @@ -44,6 +44,7 @@ See below for a set of tables and fields, expected values and what they can do | Goal | A description of what you would hope the outcome to be. When verifying output, you can review and confirm whether the strategy met that goal | | CurrencySettings | Currency settings is an array of settings for each individual currency you wish to run the strategy against | | StrategySettings | Select which strategy to run, what custom settings to load and whether the strategy can assess multiple currencies at once to make more in-depth decisions | +| FundingSettings | Defines whether individual funding settings can be used. Defines the funding exchange, asset, currencies at an individual level | | PortfolioSettings | Contains a list of global rules for the portfolio manager. CurrencySettings contain their own rules on things like how big a position is allowable, the portfolio manager rules are the same, but override any individual currency's settings | | StatisticSettings | Contains settings that impact statistics calculation. Such as the risk-free rate for the sharpe ratio | | GoCryptoTraderConfigPath | The filepath for the location of GoCryptoTrader's config path. The Backtester utilises settings from GoCryptoTrader. If unset, will utilise the default filepath via `config.DefaultFilePath`, implemented [here](/config/config.go#L1460) | @@ -56,12 +57,18 @@ See below for a set of tables and fields, expected values and what they can do | Name | The strategy to use | `rsi` | | UsesSimultaneousProcessing | This denotes whether multiple currencies are processed simultaneously with the strategy function `OnSimultaneousSignals`. Eg If you have multiple CurrencySettings and only wish to purchase BTC-USDT when XRP-DOGE is 1337, this setting is useful as you can analyse both signal events to output a purchase call for BTC | `true` | | CustomSettings | This is a map where you can enter custom settings for a strategy. The RSI strategy allows for customisation of the upper, lower and length variables to allow you to change them from 70, 30 and 14 respectively to 69, 36, 12 | `"custom-settings": { "rsi-high": 70, "rsi-low": 30, "rsi-period": 14 } ` | +| DisableUSDTracking | If `false`, will track all currencies used in your strategy against USD equivalent candles. For example, if you are running a strategy for BTC/XRP, then the GoCryptoTrader Backtester will also retreive candles data for BTC/USD and XRP/USD to then track strategy performance against a single currency. This also tracks against USDT and other USD tracked stablecoins, so one exchange supporting USDT and another BUSD will still allow unified strategy performance analysis. If disabled, will not track against USD, this can be especially helpful when running strategies under live, database and CSV based data | `false` | + + +#### Funding Config Settings + +| Key | Description | Example | +| --- | ------- | --- | | UseExchangeLevelFunding | Allows shared funding at an exchange asset level. You can set funding for `USDT` and all pairs that feature `USDT` will have access to those funds when making orders. See [this](/backtester/funding/README.md) for more information | `false` | | ExchangeLevelFunding | An array of exchange level funding settings. See below, or [this](/backtester/funding/README.md) for more information | `[]` | -| DisableUSDTracking | If `false`, will track all currencies used in your strategy against USD equivalent candles. For example, if you are running a strategy for BTC/XRP, then the GoCryptoTrader Backtester will also retreive candles data for BTC/USD and XRP/USD to then track strategy performance against a single currency. This also tracks against USDT and other USD tracked stablecoins, so one exchange supporting USDT and another BUSD will still allow unified strategy performance analysis. If disabled, will not track against USD, this can be especially helpful when running strategies under live, database and CSV based data | `false` | -##### Funding Config Settings +##### Funding Item Config Settings | Key | Description | Example | | --- | ------- | ----- | | ExchangeName | The exchange to set funds. See [here](https://github.com/thrasher-corp/gocryptotrader/blob/master/README.md) for a list of supported exchanges | `Binance` | @@ -80,18 +87,30 @@ See below for a set of tables and fields, expected values and what they can do | Base | The base of a currency | `BTC` | | Quote | The quote of a currency | `USDT` | | InitialFunds | A legacy field, will be temporarily migrated to `InitialQuoteFunds` if present in your strat config | `` | -| InitialBaseFunds | The funds that the GoCryptoTraderBacktester has for the base currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `2` | -| InitialQuoteFunds | The funds that the GoCryptoTraderBacktester has for the quote currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `10000` | -| Leverage | This struct defines the leverage rules that this specific currency setting must abide by | `1` | | BuySide | This struct defines the buying side rules this specific currency setting must abide by such as maximum purchase amount | - | | SellSide | This struct defines the selling side rules this specific currency setting must abide by such as maximum selling amount | - | | MinimumSlippagePercent | Is the lower bounds in a random number generated that make purchases more expensive, or sell events less valuable. If this value is 90, then the most a price can be affected is 10% | `90` | | MaximumSlippagePercent | Is the upper bounds in a random number generated that make purchases more expensive, or sell events less valuable. If this value is 99, then the least a price can be affected is 1%. Set both upper and lower to 100 to have no randomness applied to purchase events | `100` | -| MakerFee | The fee to use when sizing and purchasing currency | `0.001` | -| TakerFee | Unused fee for when an order is placed in the orderbook, rather than taken from the orderbook | `0.002` | +| MakerFee | The fee to use when sizing and purchasing currency. If `nil`, will lookup an exchange's fee details | `0.001` | +| TakerFee | Unused fee for when an order is placed in the orderbook, rather than taken from the orderbook. If `nil`, will lookup an exchange's fee details | `0.002` | | MaximumHoldingsRatio | When multiple currency settings are used, you may set a maximum holdings ratio to prevent having too large a stake in a single currency | `0.5` | | CanUseExchangeLimits | Will lookup exchange rules around purchase sizing eg minimum order increments of 0.0005. Note: Will retrieve up-to-date rules which may not have existed for the data you are using. Best to use this when considering to use this strategy live | `false` | | SkipCandleVolumeFitting | When placing orders, by default the BackTester will shrink an order's size to fit the candle data's volume so as to not rewrite history. Set this to `true` to ignore this and to set order size at what the portfolio manager prescribes | `false` | +| SpotSettings | An optional field which contains initial funding data for SPOT currency pairs | See SpotSettings table below | +| FuturesSettings | An optional field which contains leverage data for FUTURES currency pairs | See FuturesSettings table below | + +##### SpotSettings + +| Key | Description | Example | +| --- | ------- | ----- | +| InitialBaseFunds | The funds that the GoCryptoTraderBacktester has for the base currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `2` | +| InitialQuoteFunds | The funds that the GoCryptoTraderBacktester has for the quote currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `10000` | + +##### FuturesSettings + +| Key | Description | Example | +| --- | ------- | ----- | +| Leverage | This struct defines the leverage rules that this specific currency setting must abide by | `1` | #### PortfolioSettings diff --git a/backtester/config/examples/README.md b/backtester/config/examples/README.md index 0e973e476a2..9d868e9fc69 100644 --- a/backtester/config/examples/README.md +++ b/backtester/config/examples/README.md @@ -34,6 +34,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | dca-database-candles.strat | The same DCA strategy, but uses a database to retrieve candle data | | rsi-api-candles.strat | Runs a strategy using rsi figures to make buy or sell orders based on market figures | | t2b2-api-candles-exchange-funding.strat | Runs a more complex strategy using simultaneous signal processing, exchange level funding and MFI values to make buy or sell signals based on the two strongest and weakest MFI values | +| ftx-cash-carry.strat | Executes a cash and carry trade on FTX, buying BTC-USD while shorting the long dated futures contract BTC-20210924 | ### Want to make your own configs? Use the provided config builder under `/backtester/config/configbuilder` or modify tests under `/backtester/config/config_test.go` to generates strategy files quickly diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 8552a9f933a..70215173f29 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -247,8 +247,14 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu } err = bt.Portfolio.UpdatePNL(ev, ev.GetClosePrice()) - if err != nil && !errors.Is(err, gctorder.ErrPositionLiquidated) { - return fmt.Errorf("UpdatePNL %v", err) + if err != nil { + if errors.Is(err, gctorder.ErrPositionsNotLoadedForPair) { + // if there is no position yet, there's nothing to update + return nil + } + if !errors.Is(err, gctorder.ErrPositionLiquidated) { + return fmt.Errorf("UpdatePNL %v", err) + } } var pnl *portfolio.PNLSummary pnl, err = bt.Portfolio.GetLatestPNLForEvent(ev) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/README.md b/backtester/eventhandlers/strategies/ftxcashandcarry/README.md new file mode 100644 index 00000000000..d5743b032db --- /dev/null +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/README.md @@ -0,0 +1,68 @@ +# GoCryptoTrader Backtester: Ftxcashandcarry package + + + + +[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) +[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/ftxcashandcarry) +[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) + + +This ftxcashandcarry package is part of the GoCryptoTrader codebase. + +## This is still in active development + +You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader). + +Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) + +## FTX Cash and carry strategy overview + +### Description +Cash and carry is a strategy which takes advantage of the difference in pricing between a long-dated futures contract and a SPOT asset. +By default, this cash and carry strategy will, upon the first data event, purchase BTC-USD SPOT asset from FTX exchange and then, once filled, raise a SHORT for BTC-20210924 FUTURES contract. +On the last event, the strategy will close the SHORT position by raising a LONG of the same contract amount, thereby netting the difference in prices + +### Requirements +- At this time of writing, this strategy is only compatible with FTX +- This strategy *requires* `Simultaneous Signal Processing` aka [use-simultaneous-signal-processing](/backtester/config/README.md). +- This strategy *requires* `Exchange Level Funding` aka [use-exchange-level-funding](/backtester/config/README.md). + +### Creating a strategy config +-The long-dated futures contract will need to be part of the `currency-settings` of the contract +- Funding for purchasing SPOT assets will need to be part of `funding-settings` +- See the [example config](./config/examples/ftx-cash-carry.strat) + +### Customisation +This strategy does support strategy customisation in the following ways: + +| Field | Description | Example | +| --- | ------- | --- | +|openShortDistancePercentage| If there is no short position open, and the difference between FUTURES and SPOT pricing goes above this this percentage threshold, raise a SHORT order of the FUTURES contract | 10 | +|closeShortDistancePercentage| If there is an open SHORT position on a FUTURES contract, and the difference in FUTURES and SPOT pricing goes below this percentage threshold, close the SHORT position | 1 | + +### External Resources +- [This](https://ftxcashandcarry.com/) is a very informative site on describing what a cash and carry trade will look like + +### Please click GoDocs chevron above to view current GoDoc information for this package + +## Contribution + +Please feel free to submit any pull requests or suggest any desired features to be added. + +When submitting a PR, please abide by our coding guidelines: + ++ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). ++ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. ++ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md). ++ Pull requests need to be based on and opened against the `master` branch. + +## Donations + + + +If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to: + +***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc*** diff --git a/backtester/funding/README.md b/backtester/funding/README.md index cc3eb3aef79..c593cbb76d3 100644 --- a/backtester/funding/README.md +++ b/backtester/funding/README.md @@ -36,6 +36,9 @@ A funding item holds the initial funding, current funding, reserved funding and ### What is a funding Pair? A funding Pair consists of two funding Items, the Base and Quote. If Exchange Level Funding is disabled, the Base and Quote are linked to each other and the funds cannot be shared with other Pairs or Items. If Exchange Level Funding is enabled, the pair can access the same funds as every other currency that shares the exchange and asset type. +### What is a collateral Pair? +A collateral Pair consists of two funding Items, the Contract and Collateral. These are exclusive to FUTURES asset type and help track how much money there is, along with how many contract holdings there are + ### What does Exchange Level Funding mean? Exchange level funding allows funds to be shared during a backtesting run. If the strategy contains the two pairs BTC-USDT and BNB-USDT and the strategy sells 3 BTC for $100,000 USDT, then BNB-USDT can use that $100,000 USDT to make a purchase of $20,000 BNB. It is restricted to an exchange and asset type, so BTC used in spot, cannot be used in a futures contract (futures backtesting is not currently supported). However, the funding manager can transfer funds between exchange and asset types. @@ -67,6 +70,11 @@ No. The already existing `CurrencySettings` will populate the funding manager wi | Name | The strategy to use | `rsi` | | UsesSimultaneousProcessing | This denotes whether multiple currencies are processed simultaneously with the strategy function `OnSimultaneousSignals`. Eg If you have multiple CurrencySettings and only wish to purchase BTC-USDT when XRP-DOGE is 1337, this setting is useful as you can analyse both signal events to output a purchase call for BTC | `true` | | CustomSettings | This is a map where you can enter custom settings for a strategy. The RSI strategy allows for customisation of the upper, lower and length variables to allow you to change them from 70, 30 and 14 respectively to 69, 36, 12 | `"custom-settings": { "rsi-high": 70, "rsi-low": 30, "rsi-period": 14 } ` | + +#### Funding Settings + +| Key | Description | Example | +| --- | ------- | --- | | UseExchangeLevelFunding | This allows shared exchange funds to be used in your strategy. Requires `UsesSimultaneousProcessing` to be set to `true` to use | `false` | | ExchangeLevelFunding | This is a list of funding definitions if `UseExchangeLevelFunding` is set to true | See below table | diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 028dac834d2..8bf8d50fcf3 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -117,17 +117,10 @@ func (i *Item) MatchesExchange(item *Item) bool { // TakeProfit increases available funds for a futures asset func (i *Item) TakeProfit(amount decimal.Decimal) error { - if !i.asset.IsFutures() { - return fmt.Errorf("%v %v %v %w", i.exchange, i.asset, i.currency, errNotFutures) - } - if !i.isCollateral { + if i.asset.IsFutures() && !i.isCollateral { return fmt.Errorf("%v %v %v %w cannot add profit to contracts", i.exchange, i.asset, i.currency, ErrNotCollateral) } - summed := i.available.Add(amount) - if summed.LessThan(decimal.Zero) { - return fmt.Errorf("%w amount '%v' would take available funds '%v' to '%v'", errNotEnoughFunds, amount, i.available, summed) - } - i.available = summed + i.available = i.available.Add(amount) return nil } diff --git a/cmd/documentation/backtester_templates/backtester_config_examples_readme.tmpl b/cmd/documentation/backtester_templates/backtester_config_examples_readme.tmpl index 7db072515b3..7b4fbcab849 100644 --- a/cmd/documentation/backtester_templates/backtester_config_examples_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_config_examples_readme.tmpl @@ -16,6 +16,7 @@ | dca-database-candles.strat | The same DCA strategy, but uses a database to retrieve candle data | | rsi-api-candles.strat | Runs a strategy using rsi figures to make buy or sell orders based on market figures | | t2b2-api-candles-exchange-funding.strat | Runs a more complex strategy using simultaneous signal processing, exchange level funding and MFI values to make buy or sell signals based on the two strongest and weakest MFI values | +| ftx-cash-carry.strat | Executes a cash and carry trade on FTX, buying BTC-USD while shorting the long dated futures contract BTC-20210924 | ### Want to make your own configs? Use the provided config builder under `/backtester/config/configbuilder` or modify tests under `/backtester/config/config_test.go` to generates strategy files quickly diff --git a/cmd/documentation/backtester_templates/backtester_config_readme.tmpl b/cmd/documentation/backtester_templates/backtester_config_readme.tmpl index 696fe01b790..a8ba247ddac 100644 --- a/cmd/documentation/backtester_templates/backtester_config_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_config_readme.tmpl @@ -26,6 +26,7 @@ See below for a set of tables and fields, expected values and what they can do | Goal | A description of what you would hope the outcome to be. When verifying output, you can review and confirm whether the strategy met that goal | | CurrencySettings | Currency settings is an array of settings for each individual currency you wish to run the strategy against | | StrategySettings | Select which strategy to run, what custom settings to load and whether the strategy can assess multiple currencies at once to make more in-depth decisions | +| FundingSettings | Defines whether individual funding settings can be used. Defines the funding exchange, asset, currencies at an individual level | | PortfolioSettings | Contains a list of global rules for the portfolio manager. CurrencySettings contain their own rules on things like how big a position is allowable, the portfolio manager rules are the same, but override any individual currency's settings | | StatisticSettings | Contains settings that impact statistics calculation. Such as the risk-free rate for the sharpe ratio | | GoCryptoTraderConfigPath | The filepath for the location of GoCryptoTrader's config path. The Backtester utilises settings from GoCryptoTrader. If unset, will utilise the default filepath via `config.DefaultFilePath`, implemented [here](/config/config.go#L1460) | @@ -38,12 +39,18 @@ See below for a set of tables and fields, expected values and what they can do | Name | The strategy to use | `rsi` | | UsesSimultaneousProcessing | This denotes whether multiple currencies are processed simultaneously with the strategy function `OnSimultaneousSignals`. Eg If you have multiple CurrencySettings and only wish to purchase BTC-USDT when XRP-DOGE is 1337, this setting is useful as you can analyse both signal events to output a purchase call for BTC | `true` | | CustomSettings | This is a map where you can enter custom settings for a strategy. The RSI strategy allows for customisation of the upper, lower and length variables to allow you to change them from 70, 30 and 14 respectively to 69, 36, 12 | `"custom-settings": { "rsi-high": 70, "rsi-low": 30, "rsi-period": 14 } ` | +| DisableUSDTracking | If `false`, will track all currencies used in your strategy against USD equivalent candles. For example, if you are running a strategy for BTC/XRP, then the GoCryptoTrader Backtester will also retreive candles data for BTC/USD and XRP/USD to then track strategy performance against a single currency. This also tracks against USDT and other USD tracked stablecoins, so one exchange supporting USDT and another BUSD will still allow unified strategy performance analysis. If disabled, will not track against USD, this can be especially helpful when running strategies under live, database and CSV based data | `false` | + + +#### Funding Config Settings + +| Key | Description | Example | +| --- | ------- | --- | | UseExchangeLevelFunding | Allows shared funding at an exchange asset level. You can set funding for `USDT` and all pairs that feature `USDT` will have access to those funds when making orders. See [this](/backtester/funding/README.md) for more information | `false` | | ExchangeLevelFunding | An array of exchange level funding settings. See below, or [this](/backtester/funding/README.md) for more information | `[]` | -| DisableUSDTracking | If `false`, will track all currencies used in your strategy against USD equivalent candles. For example, if you are running a strategy for BTC/XRP, then the GoCryptoTrader Backtester will also retreive candles data for BTC/USD and XRP/USD to then track strategy performance against a single currency. This also tracks against USDT and other USD tracked stablecoins, so one exchange supporting USDT and another BUSD will still allow unified strategy performance analysis. If disabled, will not track against USD, this can be especially helpful when running strategies under live, database and CSV based data | `false` | -##### Funding Config Settings +##### Funding Item Config Settings | Key | Description | Example | | --- | ------- | ----- | | ExchangeName | The exchange to set funds. See [here](https://github.com/thrasher-corp/gocryptotrader/blob/master/README.md) for a list of supported exchanges | `Binance` | @@ -62,18 +69,30 @@ See below for a set of tables and fields, expected values and what they can do | Base | The base of a currency | `BTC` | | Quote | The quote of a currency | `USDT` | | InitialFunds | A legacy field, will be temporarily migrated to `InitialQuoteFunds` if present in your strat config | `` | -| InitialBaseFunds | The funds that the GoCryptoTraderBacktester has for the base currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `2` | -| InitialQuoteFunds | The funds that the GoCryptoTraderBacktester has for the quote currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `10000` | -| Leverage | This struct defines the leverage rules that this specific currency setting must abide by | `1` | | BuySide | This struct defines the buying side rules this specific currency setting must abide by such as maximum purchase amount | - | | SellSide | This struct defines the selling side rules this specific currency setting must abide by such as maximum selling amount | - | | MinimumSlippagePercent | Is the lower bounds in a random number generated that make purchases more expensive, or sell events less valuable. If this value is 90, then the most a price can be affected is 10% | `90` | | MaximumSlippagePercent | Is the upper bounds in a random number generated that make purchases more expensive, or sell events less valuable. If this value is 99, then the least a price can be affected is 1%. Set both upper and lower to 100 to have no randomness applied to purchase events | `100` | -| MakerFee | The fee to use when sizing and purchasing currency | `0.001` | -| TakerFee | Unused fee for when an order is placed in the orderbook, rather than taken from the orderbook | `0.002` | +| MakerFee | The fee to use when sizing and purchasing currency. If `nil`, will lookup an exchange's fee details | `0.001` | +| TakerFee | Unused fee for when an order is placed in the orderbook, rather than taken from the orderbook. If `nil`, will lookup an exchange's fee details | `0.002` | | MaximumHoldingsRatio | When multiple currency settings are used, you may set a maximum holdings ratio to prevent having too large a stake in a single currency | `0.5` | | CanUseExchangeLimits | Will lookup exchange rules around purchase sizing eg minimum order increments of 0.0005. Note: Will retrieve up-to-date rules which may not have existed for the data you are using. Best to use this when considering to use this strategy live | `false` | | SkipCandleVolumeFitting | When placing orders, by default the BackTester will shrink an order's size to fit the candle data's volume so as to not rewrite history. Set this to `true` to ignore this and to set order size at what the portfolio manager prescribes | `false` | +| SpotSettings | An optional field which contains initial funding data for SPOT currency pairs | See SpotSettings table below | +| FuturesSettings | An optional field which contains leverage data for FUTURES currency pairs | See FuturesSettings table below | + +##### SpotSettings + +| Key | Description | Example | +| --- | ------- | ----- | +| InitialBaseFunds | The funds that the GoCryptoTraderBacktester has for the base currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `2` | +| InitialQuoteFunds | The funds that the GoCryptoTraderBacktester has for the quote currency. This is only required if the strategy setting `UseExchangeLevelFunding` is `false` | `10000` | + +##### FuturesSettings + +| Key | Description | Example | +| --- | ------- | ----- | +| Leverage | This struct defines the leverage rules that this specific currency setting must abide by | `1` | #### PortfolioSettings diff --git a/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl b/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl new file mode 100644 index 00000000000..0c37c9d0070 --- /dev/null +++ b/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl @@ -0,0 +1,34 @@ +{{define "backtester eventhandlers strategies ftxcashandcarry" -}} +{{template "backtester-header" .}} +## FTX Cash and carry strategy overview + +### Description +Cash and carry is a strategy which takes advantage of the difference in pricing between a long-dated futures contract and a SPOT asset. +By default, this cash and carry strategy will, upon the first data event, purchase BTC-USD SPOT asset from FTX exchange and then, once filled, raise a SHORT for BTC-20210924 FUTURES contract. +On the last event, the strategy will close the SHORT position by raising a LONG of the same contract amount, thereby netting the difference in prices + +### Requirements +- At this time of writing, this strategy is only compatible with FTX +- This strategy *requires* `Simultaneous Signal Processing` aka [use-simultaneous-signal-processing](/backtester/config/README.md). +- This strategy *requires* `Exchange Level Funding` aka [use-exchange-level-funding](/backtester/config/README.md). + +### Creating a strategy config +-The long-dated futures contract will need to be part of the `currency-settings` of the contract +- Funding for purchasing SPOT assets will need to be part of `funding-settings` +- See the [example config](./config/examples/ftx-cash-carry.strat) + +### Customisation +This strategy does support strategy customisation in the following ways: + +| Field | Description | Example | +| --- | ------- | --- | +|openShortDistancePercentage| If there is no short position open, and the difference between FUTURES and SPOT pricing goes above this this percentage threshold, raise a SHORT order of the FUTURES contract | 10 | +|closeShortDistancePercentage| If there is an open SHORT position on a FUTURES contract, and the difference in FUTURES and SPOT pricing goes below this percentage threshold, close the SHORT position | 1 | + +### External Resources +- [This](https://ftxcashandcarry.com/) is a very informative site on describing what a cash and carry trade will look like + +### Please click GoDocs chevron above to view current GoDoc information for this package +{{template "contributions"}} +{{template "donations" .}} +{{end}} \ No newline at end of file diff --git a/cmd/documentation/backtester_templates/backtester_funding_readme.tmpl b/cmd/documentation/backtester_templates/backtester_funding_readme.tmpl index b0cdd3ac978..634867d2239 100644 --- a/cmd/documentation/backtester_templates/backtester_funding_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_funding_readme.tmpl @@ -18,6 +18,9 @@ A funding item holds the initial funding, current funding, reserved funding and ### What is a funding Pair? A funding Pair consists of two funding Items, the Base and Quote. If Exchange Level Funding is disabled, the Base and Quote are linked to each other and the funds cannot be shared with other Pairs or Items. If Exchange Level Funding is enabled, the pair can access the same funds as every other currency that shares the exchange and asset type. +### What is a collateral Pair? +A collateral Pair consists of two funding Items, the Contract and Collateral. These are exclusive to FUTURES asset type and help track how much money there is, along with how many contract holdings there are + ### What does Exchange Level Funding mean? Exchange level funding allows funds to be shared during a backtesting run. If the strategy contains the two pairs BTC-USDT and BNB-USDT and the strategy sells 3 BTC for $100,000 USDT, then BNB-USDT can use that $100,000 USDT to make a purchase of $20,000 BNB. It is restricted to an exchange and asset type, so BTC used in spot, cannot be used in a futures contract (futures backtesting is not currently supported). However, the funding manager can transfer funds between exchange and asset types. @@ -49,6 +52,11 @@ No. The already existing `CurrencySettings` will populate the funding manager wi | Name | The strategy to use | `rsi` | | UsesSimultaneousProcessing | This denotes whether multiple currencies are processed simultaneously with the strategy function `OnSimultaneousSignals`. Eg If you have multiple CurrencySettings and only wish to purchase BTC-USDT when XRP-DOGE is 1337, this setting is useful as you can analyse both signal events to output a purchase call for BTC | `true` | | CustomSettings | This is a map where you can enter custom settings for a strategy. The RSI strategy allows for customisation of the upper, lower and length variables to allow you to change them from 70, 30 and 14 respectively to 69, 36, 12 | `"custom-settings": { "rsi-high": 70, "rsi-low": 30, "rsi-period": 14 } ` | + +#### Funding Settings + +| Key | Description | Example | +| --- | ------- | --- | | UseExchangeLevelFunding | This allows shared exchange funds to be used in your strategy. Requires `UsesSimultaneousProcessing` to be set to `true` to use | `false` | | ExchangeLevelFunding | This is a list of funding definitions if `UseExchangeLevelFunding` is set to true | See below table | diff --git a/cmd/documentation/backtester_templates/backtester_readme.tmpl b/cmd/documentation/backtester_templates/backtester_readme.tmpl index 9038afc7be2..1f9a3158f2a 100644 --- a/cmd/documentation/backtester_templates/backtester_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_readme.tmpl @@ -25,14 +25,16 @@ An event-driven backtesting tool to test and iterate trading strategies using hi - Compliance manager to keep snapshots of every transaction and their changes at every interval - Exchange level funding allows funding to be shared across multiple currency pairs and to allow for complex strategy design - Fund transfer. At a strategy level, transfer funds between exchanges to allow for complex strategy design +- Backtesting support for futures asset types +- Example cash and carry spot futures strategy ## Planned Features We welcome pull requests on any feature for the Backtester! We will be especially appreciative of any contribution towards the following planned features: | Feature | Description | |---------|-------------| -| Add backtesting support for futures asset types | Spot trading is currently the only supported asset type. Futures trading greatly expands the Backtester's potential | -| Example futures pairs trading strategy | Providing a basic example will allow for esteemed traders to build and customise their own | +| Long-running application | Transform the Backtester to run a GRPC server, where commands can be sent to run Backtesting operations. Allowing for many strategies to be run, analysed and tweaked in a more efficient manner | +| Enhance config-builder | Create an application that can create strategy configs in a more visual manner and execute them via GRPC to allow for faster customisation of strategies | | Save Backtester results to database | This will allow for easier comparison of results over time | | Backtester result comparison report | Providing an executive summary of Backtester database results | | Currency correlation | Compare multiple exchange, asset, currencies for a candle interval against indicators to highlight correlated pairs for use in pairs trading | From c50291d067bda14729138f0f6e9144a4bae68ff8 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 14:52:50 +1000 Subject: [PATCH 125/171] Changes before master merge --- backtester/README.md | 1 + backtester/common/common.go | 3 +-- backtester/config/README.md | 2 +- backtester/config/config_types.go | 1 + backtester/data/data.go | 1 + backtester/eventhandlers/exchange/exchange.go | 2 +- backtester/eventhandlers/portfolio/portfolio.go | 3 ++- .../eventhandlers/statistics/statistics.go | 1 + .../eventhandlers/strategies/base/base_types.go | 3 ++- .../strategies/ftxcashandcarry/README.md | 4 ++-- .../ftxcashandcarry/ftxcashandcarry_types.go | 2 +- backtester/funding/funding.go | 7 ++++--- backtester/funding/funding_types.go | 4 +++- backtester/funding/spotpair.go | 2 ++ backtester/report/chart.go | 1 - .../backtester_config_readme.tmpl | 2 +- ...dlers_strategies_ftxcashandcarry_readme.tmpl | 4 ++-- .../backtester_templates/backtester_readme.tmpl | 1 + engine/order_manager_test.go | 1 + exchanges/ftx/ftx_test.go | 2 +- exchanges/order/futures.go | 1 + exchanges/order/futures_test.go | 17 +++++++++++------ 22 files changed, 41 insertions(+), 24 deletions(-) diff --git a/backtester/README.md b/backtester/README.md index a46bf5e7b4a..b31f9bb9de6 100644 --- a/backtester/README.md +++ b/backtester/README.md @@ -52,6 +52,7 @@ We welcome pull requests on any feature for the Backtester! We will be especiall | Feature | Description | |---------|-------------| | Long-running application | Transform the Backtester to run a GRPC server, where commands can be sent to run Backtesting operations. Allowing for many strategies to be run, analysed and tweaked in a more efficient manner | +| Leverage support | Leverage is a good way to enhance profit and loss and is important to include in strategies | | Enhance config-builder | Create an application that can create strategy configs in a more visual manner and execute them via GRPC to allow for faster customisation of strategies | | Save Backtester results to database | This will allow for easier comparison of results over time | | Backtester result comparison report | Providing an executive summary of Backtester database results | diff --git a/backtester/common/common.go b/backtester/common/common.go index f7073162f53..0bf44694cfa 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -33,9 +33,8 @@ func FitStringToLimit(str, spacer string, limit int, upper bool) string { if limResp < 0 { if limit-3 > 0 { return str[0:limit-3] + "..." - } else { - return str[0:limit] } + return str[0:limit] } spacerLen := len(spacer) for i := 0; i < limResp; i++ { diff --git a/backtester/config/README.md b/backtester/config/README.md index 025f6b95b7a..7458a2f3770 100644 --- a/backtester/config/README.md +++ b/backtester/config/README.md @@ -47,7 +47,6 @@ See below for a set of tables and fields, expected values and what they can do | FundingSettings | Defines whether individual funding settings can be used. Defines the funding exchange, asset, currencies at an individual level | | PortfolioSettings | Contains a list of global rules for the portfolio manager. CurrencySettings contain their own rules on things like how big a position is allowable, the portfolio manager rules are the same, but override any individual currency's settings | | StatisticSettings | Contains settings that impact statistics calculation. Such as the risk-free rate for the sharpe ratio | -| GoCryptoTraderConfigPath | The filepath for the location of GoCryptoTrader's config path. The Backtester utilises settings from GoCryptoTrader. If unset, will utilise the default filepath via `config.DefaultFilePath`, implemented [here](/config/config.go#L1460) | #### Strategy Settings @@ -69,6 +68,7 @@ See below for a set of tables and fields, expected values and what they can do ##### Funding Item Config Settings + | Key | Description | Example | | --- | ------- | ----- | | ExchangeName | The exchange to set funds. See [here](https://github.com/thrasher-corp/gocryptotrader/blob/master/README.md) for a list of supported exchanges | `Binance` | diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 6639f6eea44..19114fd0cd9 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -50,6 +50,7 @@ type DataSettings struct { CSVData *CSVData `json:"csv-data,omitempty"` } +// FundingSettings contains funding details for individual currencies type FundingSettings struct { UseExchangeLevelFunding bool `json:"use-exchange-level-funding"` ExchangeLevelFunding []ExchangeLevelFunding `json:"exchange-level-funding,omitempty"` diff --git a/backtester/data/data.go b/backtester/data/data.go index 6a28c7a31a3..a70ff209542 100644 --- a/backtester/data/data.go +++ b/backtester/data/data.go @@ -115,6 +115,7 @@ func (b *Base) List() []common.DataEventHandler { return b.stream[b.offset:] } +// IsLastEvent determines whether the latest event is the last event func (b *Base) IsLastEvent() bool { return b.latest != nil && b.latest.GetOffset() == int64(len(b.stream)) } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index b74c3f4da56..724229569cb 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -28,6 +28,7 @@ func (e *Exchange) Reset() { *e = Exchange{} } +// ErrDoNothing returns when its an issue to do nothing for an event var ErrDoNothing = errors.New("received Do Nothing direction") // ExecuteOrder assesses the portfolio manager's order event and if it passes validation @@ -75,7 +76,6 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } // update local records cr.Liquidate() - } else { pr, err := funds.PairReleaser() if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index e89a23646a2..5e3ad25744f 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -136,7 +136,7 @@ func cannotPurchase(ev signal.Event, o *order.Order) (*order.Order, error) { return nil, common.ErrNilEvent } if o == nil { - return nil, fmt.Errorf("%w recieved nil order for %v %v %v", common.ErrNilArguments, ev.GetExchange(), ev.GetAssetType(), ev.Pair()) + return nil, fmt.Errorf("%w received nil order for %v %v %v", common.ErrNilArguments, ev.GetExchange(), ev.GetAssetType(), ev.Pair()) } o.AppendReason(notEnoughFundsTo + " " + ev.GetDirection().Lower()) switch ev.GetDirection() { @@ -638,6 +638,7 @@ func (p *Portfolio) CheckLiquidationStatus(ev common.DataEventHandler, collatera return nil } +// CreateLiquidationOrdersForExchange creates liquidation orders, for any that exist on the same exchange where a liquidation is occurring func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandler, funds funding.IFundingManager) ([]order.Event, error) { if ev == nil { return nil, common.ErrNilEvent diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index b96949c8257..495e08f7aa1 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -133,6 +133,7 @@ func (s *Statistic) AddHoldingsForTime(h *holdings.Holding) error { return fmt.Errorf("%v %v %v %w %v", h.Exchange, h.Asset, h.Pair, errNoDataAtOffset, h.Offset) } +// AddPNLForTime stores PNL data for tracking purposes func (s *Statistic) AddPNLForTime(pnl *portfolio.PNLSummary) error { if pnl == nil { return fmt.Errorf("%w requires PNL", common.ErrNilArguments) diff --git a/backtester/eventhandlers/strategies/base/base_types.go b/backtester/eventhandlers/strategies/base/base_types.go index 2f2e096f195..3d8e89a6d3a 100644 --- a/backtester/eventhandlers/strategies/base/base_types.go +++ b/backtester/eventhandlers/strategies/base/base_types.go @@ -8,7 +8,8 @@ var ( // ErrSimultaneousProcessingNotSupported used when strategy does not support simultaneous processing // but start config is set to use it ErrSimultaneousProcessingNotSupported = errors.New("does not support simultaneous processing and could not be loaded") - ErrSimultaneousProcessingOnly = errors.New("this strategy only supports simultaneous processing") + // ErrSimultaneousProcessingOnly is raised when a strategy is improperly configured + ErrSimultaneousProcessingOnly = errors.New("this strategy only supports simultaneous processing") // ErrStrategyNotFound used when strategy specified in start config does not exist ErrStrategyNotFound = errors.New("not found. Please ensure the strategy-settings field 'name' is spelled properly in your .start config") // ErrInvalidCustomSettings used when bad custom settings are found in the start config diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/README.md b/backtester/eventhandlers/strategies/ftxcashandcarry/README.md index d5743b032db..9acae0e4645 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/README.md +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/README.md @@ -40,8 +40,8 @@ This strategy does support strategy customisation in the following ways: | Field | Description | Example | | --- | ------- | --- | -|openShortDistancePercentage| If there is no short position open, and the difference between FUTURES and SPOT pricing goes above this this percentage threshold, raise a SHORT order of the FUTURES contract | 10 | -|closeShortDistancePercentage| If there is an open SHORT position on a FUTURES contract, and the difference in FUTURES and SPOT pricing goes below this percentage threshold, close the SHORT position | 1 | +| openShortDistancePercentage | If there is no short position open, and the difference between FUTURES and SPOT pricing goes above this this percentage threshold, raise a SHORT order of the FUTURES contract | 10 | +| closeShortDistancePercentage | If there is an open SHORT position on a FUTURES contract, and the difference in FUTURES and SPOT pricing goes below this percentage threshold, close the SHORT position | 1 | ### External Resources - [This](https://ftxcashandcarry.com/) is a very informative site on describing what a cash and carry trade will look like diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go index 4d336185c22..7eb0e4bbbd7 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_types.go @@ -10,7 +10,7 @@ import ( const ( // Name is the strategy name Name = "ftx-cash-carry" - description = `The relative strength index is a technical indicator used in the analysis of financial markets. It is intended to chart the current and historical strength or weakness of a stock or market based on the closing prices of a recent trading period` + description = `A cash and carry trade (or basis trading) consists in taking advantage of the premium of a futures contract over the spot price. For example if Ethereum Futures are trading well above its Spot price (contango) you could perform an arbitrage and take advantage of this opportunity.` exchangeName = "ftx" openShortDistancePercentageString = "openShortDistancePercentage" closeShortDistancePercentageString = "closeShortDistancePercentage" diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index e8c664148dd..0144469737a 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -495,7 +495,6 @@ func (f *FundManager) getFundingForEAP(exch string, a asset.Item, p currency.Pai return nil, fmt.Errorf("quote %v %w", p.Quote, ErrFundsNotFound) } return &resp, nil - } return nil, fmt.Errorf("%v %v %v %w", exch, a, p, ErrFundsNotFound) @@ -550,6 +549,8 @@ func (f *FundManager) GetAllFunding() []BasicItem { return result } +// UpdateCollateral will recalculate collateral for an exchange +// based on the event passed in func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { if ev == nil { return common.ErrNilEvent @@ -624,6 +625,7 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { return fmt.Errorf("%w to allocate %v to %v %v %v", ErrFundsNotFound, collateralAmount, ev.GetExchange(), ev.GetAssetType(), futureCurrency) } +// HasFutures returns whether the funding manager contains any futures assets func (f *FundManager) HasFutures() bool { for i := range f.items { if f.items[i].isCollateral || f.items[i].asset.IsFutures() { @@ -651,9 +653,8 @@ func (f *FundManager) HasExchangeBeenLiquidated(ev common.EventHandler) bool { for i := range f.items { if ev.GetExchange() == f.items[i].exchange { return f.items[i].isLiquidated - } else { - continue } + continue } return false } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 4fa774d4241..86c6d838bcf 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -104,6 +104,7 @@ type IPairReleaser interface { Liquidate() } +// ICollateralReleaser limits funding usage for exchange event handling type ICollateralReleaser interface { ICollateralReader UpdateContracts(order.Side, decimal.Decimal) error @@ -201,7 +202,8 @@ type ItemSnapshot struct { Breakdown []CurrencyContribution } -// TODO look into this +// CurrencyContribution helps breakdown how a USD value +// determines its number type CurrencyContribution struct { Currency currency.Code USD decimal.Decimal diff --git a/backtester/funding/spotpair.go b/backtester/funding/spotpair.go index 1f1159df1c4..343fe0aaac0 100644 --- a/backtester/funding/spotpair.go +++ b/backtester/funding/spotpair.go @@ -136,10 +136,12 @@ func (p *SpotPair) FundReader() IFundReader { return p } +// GetPairReader returns an interface of a SpotPair func (p *SpotPair) GetPairReader() (IPairReader, error) { return p, nil } +// GetCollateralReader returns an error because its not collateral func (p *SpotPair) GetCollateralReader() (ICollateralReader, error) { return nil, ErrNotCollateral } diff --git a/backtester/report/chart.go b/backtester/report/chart.go index df38d7ca503..1b42dc37dd0 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -116,7 +116,6 @@ func createPNLCharts(items map[string]map[asset.Item]map[currency.Pair]*statisti response.Data = append(response.Data, unrealisedPNL, realisedPNL) } } - } return response, nil } diff --git a/cmd/documentation/backtester_templates/backtester_config_readme.tmpl b/cmd/documentation/backtester_templates/backtester_config_readme.tmpl index a8ba247ddac..7336823b47f 100644 --- a/cmd/documentation/backtester_templates/backtester_config_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_config_readme.tmpl @@ -29,7 +29,6 @@ See below for a set of tables and fields, expected values and what they can do | FundingSettings | Defines whether individual funding settings can be used. Defines the funding exchange, asset, currencies at an individual level | | PortfolioSettings | Contains a list of global rules for the portfolio manager. CurrencySettings contain their own rules on things like how big a position is allowable, the portfolio manager rules are the same, but override any individual currency's settings | | StatisticSettings | Contains settings that impact statistics calculation. Such as the risk-free rate for the sharpe ratio | -| GoCryptoTraderConfigPath | The filepath for the location of GoCryptoTrader's config path. The Backtester utilises settings from GoCryptoTrader. If unset, will utilise the default filepath via `config.DefaultFilePath`, implemented [here](/config/config.go#L1460) | #### Strategy Settings @@ -51,6 +50,7 @@ See below for a set of tables and fields, expected values and what they can do ##### Funding Item Config Settings + | Key | Description | Example | | --- | ------- | ----- | | ExchangeName | The exchange to set funds. See [here](https://github.com/thrasher-corp/gocryptotrader/blob/master/README.md) for a list of supported exchanges | `Binance` | diff --git a/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl b/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl index 0c37c9d0070..e0aa19b29a5 100644 --- a/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl @@ -22,8 +22,8 @@ This strategy does support strategy customisation in the following ways: | Field | Description | Example | | --- | ------- | --- | -|openShortDistancePercentage| If there is no short position open, and the difference between FUTURES and SPOT pricing goes above this this percentage threshold, raise a SHORT order of the FUTURES contract | 10 | -|closeShortDistancePercentage| If there is an open SHORT position on a FUTURES contract, and the difference in FUTURES and SPOT pricing goes below this percentage threshold, close the SHORT position | 1 | +| openShortDistancePercentage | If there is no short position open, and the difference between FUTURES and SPOT pricing goes above this this percentage threshold, raise a SHORT order of the FUTURES contract | 10 | +| closeShortDistancePercentage | If there is an open SHORT position on a FUTURES contract, and the difference in FUTURES and SPOT pricing goes below this percentage threshold, close the SHORT position | 1 | ### External Resources - [This](https://ftxcashandcarry.com/) is a very informative site on describing what a cash and carry trade will look like diff --git a/cmd/documentation/backtester_templates/backtester_readme.tmpl b/cmd/documentation/backtester_templates/backtester_readme.tmpl index 1f9a3158f2a..dac693aca63 100644 --- a/cmd/documentation/backtester_templates/backtester_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_readme.tmpl @@ -34,6 +34,7 @@ We welcome pull requests on any feature for the Backtester! We will be especiall | Feature | Description | |---------|-------------| | Long-running application | Transform the Backtester to run a GRPC server, where commands can be sent to run Backtesting operations. Allowing for many strategies to be run, analysed and tweaked in a more efficient manner | +| Leverage support | Leverage is a good way to enhance profit and loss and is important to include in strategies | | Enhance config-builder | Create an application that can create strategy configs in a more visual manner and execute them via GRPC to allow for faster customisation of strategies | | Save Backtester results to database | This will allow for easier comparison of results over time | | Backtester result comparison report | Providing an executive summary of Backtester database results | diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index ccccd954571..8563bb72be9 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -1400,6 +1400,7 @@ func TestUpdateExisting(t *testing.T) { if !errors.Is(err, ErrOrderNotFound) { t.Errorf("received '%v', expected '%v'", err, ErrOrderNotFound) } + od.Exchange = testExchange od.AssetType = asset.Futures od.ID = "123" od.Pair = currency.NewPair(currency.BTC, currency.USDT) diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index ca11698d90f..5a392669702 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1004,7 +1004,7 @@ func TestGetPublicOptionsTrades(t *testing.T) { if err != nil { t.Error(err) } - if len(result) != 5 { + if len(result) > 5 { t.Error("limit of 5 should return 5 items") } _, err = f.GetPublicOptionsTrades(context.Background(), diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b85abe92a93..b59a599a862 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -820,6 +820,7 @@ func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, erro pnlHistory[i].Direction = entry.Direction pnlHistory[i].Price = entry.Price pnlHistory[i].Status = entry.Status + pnlHistory[i].Fee = entry.Fee if entry.IsOrder { pnlHistory[i].IsOrder = true } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 5dd36e69d1c..36245a0ce87 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -39,7 +39,9 @@ func (f *FakePNL) GetCurrencyForRealisedPNL(realisedAsset asset.Item, realisedPa func TestUpsertPNLEntry(t *testing.T) { t.Parallel() var results []PNLResult - result := &PNLResult{} + result := &PNLResult{ + IsOrder: true, + } _, err := upsertPNLEntry(results, result) if !errors.Is(err, errTimeUnset) { t.Error(err) @@ -176,7 +178,7 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if f.currentDirection != UnknownSide { + if f.currentDirection != SideNA { t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) } if f.status != Closed { @@ -187,7 +189,7 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, ErrPositionClosed) { t.Error(err) } - if f.currentDirection != UnknownSide { + if f.currentDirection != SideNA { t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) } if f.status != Closed { @@ -607,29 +609,32 @@ func TestCalculateRealisedPNL(t *testing.T) { t.Parallel() result := calculateRealisedPNL(nil) if !result.IsZero() { - t.Error("expected zero") + t.Errorf("received '%v' expected '0'", result) } result = calculateRealisedPNL([]PNLResult{ { + IsOrder: true, RealisedPNLBeforeFees: decimal.NewFromInt(1337), }, }) if !result.Equal(decimal.NewFromInt(1337)) { - t.Error("expected 1337") + t.Errorf("received '%v' expected '1337'", result) } result = calculateRealisedPNL([]PNLResult{ { + IsOrder: true, RealisedPNLBeforeFees: decimal.NewFromInt(1339), Fee: decimal.NewFromInt(2), }, { + IsOrder: true, RealisedPNLBeforeFees: decimal.NewFromInt(2), Fee: decimal.NewFromInt(2), }, }) if !result.Equal(decimal.NewFromInt(1337)) { - t.Error("expected 1337") + t.Errorf("received '%v' expected '1337'", result) } } From a2b3c2a437e2a63c9f6d1b80165c623e736d0c9d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 16:39:55 +1000 Subject: [PATCH 126/171] Lint and Tests --- backtester/common/common.go | 2 +- backtester/common/common_test.go | 2 +- backtester/config/config_test.go | 4 +- backtester/data/data_test.go | 1 - backtester/engine/setup.go | 1 - backtester/eventhandlers/exchange/exchange.go | 10 ++-- .../eventhandlers/portfolio/portfolio.go | 22 +++---- .../eventhandlers/portfolio/portfolio_test.go | 5 +- .../eventhandlers/portfolio/size/size.go | 1 - .../statistics/fundingstatistics_test.go | 1 - .../eventhandlers/statistics/printresults.go | 8 +-- .../ftxcashandcarry/ftxcashandcarry.go | 6 +- .../ftxcashandcarry/ftxcashandcarry_test.go | 28 ++++----- backtester/eventtypes/event/event_test.go | 1 - backtester/funding/collateralpair_test.go | 44 +++++--------- backtester/funding/funding.go | 12 ++-- backtester/funding/spotpair_test.go | 21 +++---- backtester/report/chart.go | 6 +- backtester/report/chart_test.go | 6 +- engine/order_manager_test.go | 4 ++ engine/rpcserver_test.go | 41 +++++++++---- exchanges/order/futures.go | 53 ++++++++-------- exchanges/order/futures_test.go | 60 +++++++++---------- exchanges/order/futures_types.go | 6 +- 24 files changed, 176 insertions(+), 169 deletions(-) diff --git a/backtester/common/common.go b/backtester/common/common.go index 0bf44694cfa..9b07a2f8ecf 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -38,7 +38,7 @@ func FitStringToLimit(str, spacer string, limit int, upper bool) string { } spacerLen := len(spacer) for i := 0; i < limResp; i++ { - str = str + spacer + str += spacer for j := 0; j < spacerLen; j++ { if j > 0 { // prevent clever people from going beyond diff --git a/backtester/common/common_test.go b/backtester/common/common_test.go index c39f321c4db..f96319ebad1 100644 --- a/backtester/common/common_test.go +++ b/backtester/common/common_test.go @@ -98,7 +98,7 @@ func TestFitStringToLimit(t *testing.T) { t.Parallel() result := FitStringToLimit(test.str, test.sep, test.limit, test.upper) if result != test.expected { - t.Errorf("recieved '%v' expected '%v'", result, test.expected) + t.Errorf("received '%v' expected '%v'", result, test.expected) } }) } diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 05dbae2b971..0e4c7344cee 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -25,7 +25,7 @@ const ( testExchange = "ftx" dca = "dollarcostaverage" // change this if you modify a config and want it to save to the example folder - saveConfig = !false + saveConfig = false ) var ( @@ -1270,7 +1270,7 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(p, "examples", "ftx-cash-carry.strat"), result, 0770) + err = os.WriteFile(filepath.Join(p, "examples", "ftx-cash-carry.strat"), result, file.DefaultPermissionOctal) if err != nil { t.Error(err) } diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index 39bef31e423..3793e322cf9 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -69,7 +69,6 @@ func TestBaseDataFunctions(t *testing.T) { d.Reset() d.GetStream() d.SortStream() - } func TestSetup(t *testing.T) { diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index c6faf8955b2..e877bee2c4f 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -231,7 +231,6 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if cfg.CurrencySettings[i].FuturesDetails != nil { portSet.MaximumOrdersWithLeverageRatio = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrdersWithLeverageRatio portSet.MaxLeverageRate = cfg.CurrencySettings[i].FuturesDetails.Leverage.MaximumOrderLeverageRate - } portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a][curr] = portSet if cfg.CurrencySettings[i].MakerFee != nil && diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 320b8c688d9..a864672d77c 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -70,14 +70,16 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if o.IsLiquidating() { // Liquidation occurs serverside if o.GetAssetType().IsFutures() { - cr, err := funds.CollateralReleaser() + var cr funding.ICollateralReleaser + cr, err = funds.CollateralReleaser() if err != nil { return f, err } // update local records cr.Liquidate() } else { - pr, err := funds.PairReleaser() + var pr funding.IPairReleaser + pr, err = funds.PairReleaser() if err != nil { return f, err } @@ -340,7 +342,7 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal } var orderID string p := price.InexactFloat64() - fee := f.ExchangeFee.InexactFloat64() + fee := f.GetExchangeFee().InexactFloat64() o := &gctorder.Submit{ Price: p, Amount: amount.InexactFloat64(), @@ -367,7 +369,7 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal submitResponse := gctorder.SubmitResponse{ IsOrderPlaced: true, OrderID: u.String(), - Rate: f.Amount.InexactFloat64(), + Rate: f.GetAmount().InexactFloat64(), Fee: fee, Cost: p, FullyMatched: true, diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 5e3ad25744f..04e68cd8f46 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -254,7 +254,8 @@ func (p *Portfolio) OnFill(ev fill.Event, funds funding.IFundReleaser) (fill.Eve var err error if ev.GetAssetType() == asset.Spot { - fp, err := funds.PairReleaser() + var fp funding.IPairReleaser + fp, err = funds.PairReleaser() if err != nil { return nil, err } @@ -557,13 +558,14 @@ func (p *Portfolio) TrackFuturesOrder(ev fill.Event, fund funding.IFundReleaser) return nil, fmt.Errorf("%w should not happen", errNoHoldings) } amount := decimal.NewFromFloat(detail.Amount) - if ev.IsLiquidated() { + switch { + case ev.IsLiquidated(): collateralReleaser.Liquidate() err = settings.FuturesTracker.Liquidate(ev.GetClosePrice(), ev.GetTime()) if err != nil { return nil, err } - } else if pos[len(pos)-1].OpeningDirection != detail.Side { + case pos[len(pos)-1].OpeningDirection != detail.Side: err = collateralReleaser.TakeProfit(amount, pos[len(pos)-1].RealisedPNL) if err != nil { return nil, err @@ -572,7 +574,7 @@ func (p *Portfolio) TrackFuturesOrder(ev fill.Event, fund funding.IFundReleaser) if err != nil { return nil, fmt.Errorf("%v %v %v %w", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } - } else { + default: err = collateralReleaser.UpdateContracts(detail.Side, amount) if err != nil { return nil, err @@ -797,7 +799,7 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { } // GetUnrealisedPNL returns a basic struct containing unrealised PNL -func (p PNLSummary) GetUnrealisedPNL() BasicPNLResult { +func (p *PNLSummary) GetUnrealisedPNL() BasicPNLResult { return BasicPNLResult{ Time: p.Result.Time, PNL: p.Result.UnrealisedPNL, @@ -806,7 +808,7 @@ func (p PNLSummary) GetUnrealisedPNL() BasicPNLResult { } // GetRealisedPNL returns a basic struct containing realised PNL -func (p PNLSummary) GetRealisedPNL() BasicPNLResult { +func (p *PNLSummary) GetRealisedPNL() BasicPNLResult { return BasicPNLResult{ Time: p.Result.Time, PNL: p.Result.RealisedPNL, @@ -815,21 +817,21 @@ func (p PNLSummary) GetRealisedPNL() BasicPNLResult { } // GetExposure returns the position exposure -func (p PNLSummary) GetExposure() decimal.Decimal { +func (p *PNLSummary) GetExposure() decimal.Decimal { return p.Result.Exposure } // GetCollateralCurrency returns the collateral currency -func (p PNLSummary) GetCollateralCurrency() currency.Code { +func (p *PNLSummary) GetCollateralCurrency() currency.Code { return p.CollateralCurrency } // GetDirection returns the direction -func (p PNLSummary) GetDirection() gctorder.Side { +func (p *PNLSummary) GetDirection() gctorder.Side { return p.Result.Direction } // GetPositionStatus returns the position status -func (p PNLSummary) GetPositionStatus() gctorder.Status { +func (p *PNLSummary) GetPositionStatus() gctorder.Status { return p.Result.Status } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index b963acd5504..9769dd1236e 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -786,6 +786,10 @@ func TestCalculatePNL(t *testing.T) { }, }, }, false) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } + err = s.FuturesTracker.TrackNewOrder(od) if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) @@ -895,7 +899,6 @@ func TestTrackFuturesOrder(t *testing.T) { if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v", err, expectedError) } - } func TestGetHoldingsForTime(t *testing.T) { diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index eab884946ea..b9c40c82d5d 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -61,7 +61,6 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc default: return retOrder, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } - // amount = amount.Round(8) if amount.LessThanOrEqual(decimal.Zero) { return retOrder, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } diff --git a/backtester/eventhandlers/statistics/fundingstatistics_test.go b/backtester/eventhandlers/statistics/fundingstatistics_test.go index 7804f48512f..3f5e47ef7e8 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics_test.go +++ b/backtester/eventhandlers/statistics/fundingstatistics_test.go @@ -193,7 +193,6 @@ func TestCalculateIndividualFundingStatistics(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } - } func TestFundingStatisticsPrintResults(t *testing.T) { diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 65c59ff2b32..f853c769186 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -94,7 +94,7 @@ func (s *Statistic) PrintAllEventsChronologically() { currencyStatistic.Events[i].FillEvent.GetClosePrice().Round(8), currencyStatistic.Events[i].FillEvent.GetDirection()) msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) - msg = msg + common.ColourDefault + msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) } else { // successful order! @@ -114,7 +114,7 @@ func (s *Statistic) PrintAllEventsChronologically() { currencyStatistic.Events[i].FillEvent.GetTotal().Round(8), currencyStatistic.Events[i].FillEvent.GetDirection()) msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) - msg = msg + common.ColourDefault + msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) } case currencyStatistic.Events[i].SignalEvent != nil: @@ -125,7 +125,7 @@ func (s *Statistic) PrintAllEventsChronologically() { fSIL(currencyStatistic.Events[i].SignalEvent.Pair().String(), limit14), currencyStatistic.Events[i].SignalEvent.GetClosePrice().Round(8)) msg = addReason(currencyStatistic.Events[i].SignalEvent.GetReason(), msg) - msg = msg + common.ColourDefault + msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].SignalEvent.GetTime(), msg) case currencyStatistic.Events[i].DataEvent != nil: msg := fmt.Sprintf("%v %v%v%v| Price: $%v", @@ -135,7 +135,7 @@ func (s *Statistic) PrintAllEventsChronologically() { fSIL(currencyStatistic.Events[i].DataEvent.Pair().String(), limit14), currencyStatistic.Events[i].DataEvent.GetClosePrice().Round(8)) msg = addReason(currencyStatistic.Events[i].DataEvent.GetReason(), msg) - msg = msg + common.ColourDefault + msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].DataEvent.GetTime(), msg) default: errs = append(errs, fmt.Errorf(common.ColourError+"%v%v%v unexpected data received %+v"+common.ColourDefault, exch, a, fSIL(pair.String(), limit14), currencyStatistic.Events[i])) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 3a8269835b6..7284ae607b1 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -56,7 +56,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra return nil, errNoSignals } if f == nil { - return nil, fmt.Errorf("%w missing funding transferer", common.ErrNilArguments) + return nil, fmt.Errorf("%w missing funding transferred", common.ErrNilArguments) } if p == nil { return nil, fmt.Errorf("%w missing portfolio handler", common.ErrNilArguments) @@ -87,7 +87,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra sp := v.spotSignal.Latest().GetClosePrice() diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(decimal.NewFromInt(100)) futuresSignal.AppendReasonf("Futures Spot Difference: %v%%", diffBetweenFuturesSpot) - if pos != nil && pos[len(pos)-1].Status == order.Open { + if len(pos) > 0 && pos[len(pos)-1].Status == order.Open { futuresSignal.AppendReasonf("Unrealised PNL: %v %v", pos[len(pos)-1].UnrealisedPNL, pos[len(pos)-1].Underlying) } if f.HasExchangeBeenLiquidated(&spotSignal) || f.HasExchangeBeenLiquidated(&futuresSignal) { @@ -161,7 +161,7 @@ func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { if len(d) == 0 { return nil, errNoSignals } - var response = make(map[currency.Pair]cashCarrySignals) + var response = make(map[currency.Pair]cashCarrySignals, len(d)) for i := range d { l := d[i].Latest() if !strings.EqualFold(l.GetExchange(), exchangeName) { diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 98ca8873ed2..8b3af5fc39e 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -13,7 +13,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" eventkline "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/kline" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/currency" @@ -142,11 +141,12 @@ func TestSortSignals(t *testing.T) { d2 := data.Base{} d2.SetStream([]common.DataEventHandler{&eventkline.Kline{ Base: event.Base{ - Exchange: exch, - Time: dInsert, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: asset.Futures, + Exchange: exch, + Time: dInsert, + Interval: gctkline.OneDay, + CurrencyPair: currency.NewPair(currency.DOGE, currency.XRP), + AssetType: asset.Futures, + UnderlyingPair: p, }, Open: decimal.NewFromInt(1337), Close: decimal.NewFromInt(1337), @@ -154,12 +154,16 @@ func TestSortSignals(t *testing.T) { High: decimal.NewFromInt(1337), Volume: decimal.NewFromInt(1337), }}) - d.Next() - da = &datakline.DataFromKline{ + d2.Next() + da2 := &datakline.DataFromKline{ Item: gctkline.Item{}, - Base: d, + Base: d2, RangeHolder: &gctkline.IntervalRangeHolder{}, } + _, err = sortSignals([]data.Handler{da, da2}) + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } } func TestCreateSignals(t *testing.T) { @@ -216,8 +220,6 @@ func TestCreateSignals(t *testing.T) { t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) } caseTested = true - } else { - } } if !caseTested { @@ -241,8 +243,6 @@ func TestCreateSignals(t *testing.T) { t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) } caseTested = true - } else { - } } if !caseTested { @@ -266,8 +266,6 @@ func TestCreateSignals(t *testing.T) { t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) } caseTested = true - } else { - } } if !caseTested { diff --git a/backtester/eventtypes/event/event_test.go b/backtester/eventtypes/event/event_test.go index 2b417b38b07..362966bdd42 100644 --- a/backtester/eventtypes/event/event_test.go +++ b/backtester/eventtypes/event/event_test.go @@ -23,7 +23,6 @@ func TestEvent_AppendWhy(t *testing.T) { if y != "test. test" { t.Error("expected 'test. test'") } - } func TestEvent_GetAssetType(t *testing.T) { diff --git a/backtester/funding/collateralpair_test.go b/backtester/funding/collateralpair_test.go index 74d17bd0a73..00e7978d821 100644 --- a/backtester/funding/collateralpair_test.go +++ b/backtester/funding/collateralpair_test.go @@ -44,9 +44,8 @@ func TestCollateralCollateralCurrency(t *testing.T) { c := &CollateralPair{ collateral: &Item{currency: currency.DOGE}, } - doge := c.CollateralCurrency() - if !doge.Equal(currency.DOGE) { - t.Errorf("recevied '%v' expected '%v'", doge, currency.DOGE) + if !c.CollateralCurrency().Equal(currency.DOGE) { + t.Errorf("recevied '%v' expected '%v'", c.CollateralCurrency(), currency.DOGE) } } @@ -55,9 +54,8 @@ func TestCollateralContractCurrency(t *testing.T) { c := &CollateralPair{ contract: &Item{currency: currency.DOGE}, } - doge := c.ContractCurrency() - if !doge.Equal(currency.DOGE) { - t.Errorf("recevied '%v' expected '%v'", doge, currency.DOGE) + if !c.ContractCurrency().Equal(currency.DOGE) { + t.Errorf("recevied '%v' expected '%v'", c.ContractCurrency(), currency.DOGE) } } @@ -66,9 +64,8 @@ func TestCollateralInitialFunds(t *testing.T) { c := &CollateralPair{ collateral: &Item{initialFunds: decimal.NewFromInt(1337)}, } - f := c.InitialFunds() - if !f.Equal(decimal.NewFromInt(1337)) { - t.Errorf("recevied '%v' expected '%v'", f, decimal.NewFromInt(1337)) + if !c.InitialFunds().Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", c.InitialFunds(), decimal.NewFromInt(1337)) } } @@ -77,9 +74,8 @@ func TestCollateralAvailableFunds(t *testing.T) { c := &CollateralPair{ collateral: &Item{available: decimal.NewFromInt(1337)}, } - f := c.AvailableFunds() - if !f.Equal(decimal.NewFromInt(1337)) { - t.Errorf("recevied '%v' expected '%v'", f, decimal.NewFromInt(1337)) + if !c.AvailableFunds().Equal(decimal.NewFromInt(1337)) { + t.Errorf("recevied '%v' expected '%v'", c.AvailableFunds(), decimal.NewFromInt(1337)) } } @@ -89,10 +85,8 @@ func TestCollateralGetPairReader(t *testing.T) { contract: &Item{}, collateral: &Item{}, } - expectedError := ErrNotPair - _, err := c.GetPairReader() - if !errors.Is(err, expectedError) { - t.Errorf("recevied '%v' expected '%v'", err, expectedError) + if _, err := c.GetPairReader(); !errors.Is(err, ErrNotPair) { + t.Errorf("recevied '%v' expected '%v'", err, ErrNotPair) } } @@ -191,8 +185,7 @@ func TestCollateralFundReader(t *testing.T) { c := &CollateralPair{ collateral: &Item{available: decimal.NewFromInt(1337)}, } - cr := c.FundReader() - if cr != c { + if c.FundReader() != c { t.Error("expected the same thing") } } @@ -203,10 +196,8 @@ func TestCollateralPairReleaser(t *testing.T) { collateral: &Item{}, contract: &Item{}, } - expectedError := ErrNotPair - _, err := c.PairReleaser() - if !errors.Is(err, expectedError) { - t.Errorf("recevied '%v' expected '%v'", err, expectedError) + if _, err := c.PairReleaser(); !errors.Is(err, ErrNotPair) { + t.Errorf("recevied '%v' expected '%v'", err, ErrNotPair) } } @@ -215,8 +206,7 @@ func TestCollateralFundReserver(t *testing.T) { c := &CollateralPair{ collateral: &Item{available: decimal.NewFromInt(1337)}, } - cr := c.FundReserver() - if cr != c { + if c.FundReserver() != c { t.Error("expected the same thing") } } @@ -228,8 +218,7 @@ func TestCollateralCollateralReleaser(t *testing.T) { contract: &Item{}, } var expectedError error - _, err := c.CollateralReleaser() - if !errors.Is(err, expectedError) { + if _, err := c.CollateralReleaser(); !errors.Is(err, expectedError) { t.Errorf("recevied '%v' expected '%v'", err, expectedError) } } @@ -239,8 +228,7 @@ func TestCollateralFundReleaser(t *testing.T) { c := &CollateralPair{ collateral: &Item{available: decimal.NewFromInt(1337)}, } - cr := c.FundReleaser() - if cr != c { + if c.FundReleaser() != c { t.Error("expected the same thing") } } diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 28089d1759f..2dbbf7ed9c8 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -100,8 +100,7 @@ func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) err pairedWith: item, isCollateral: true, } - err := f.AddItem(collateral) - if err != nil { + if err := f.AddItem(collateral); err != nil { return err } item.pairedWith = collateral @@ -527,7 +526,7 @@ func (f *FundManager) Liquidate(ev common.EventHandler) { // GetAllFunding returns basic representations of all current // holdings from the latest point func (f *FundManager) GetAllFunding() []BasicItem { - var result []BasicItem + result := make([]BasicItem, len(f.items)) for i := range f.items { var usd decimal.Decimal if f.items[i].trackingCandles != nil { @@ -536,7 +535,7 @@ func (f *FundManager) GetAllFunding() []BasicItem { usd = latest.GetClosePrice() } } - result = append(result, BasicItem{ + result[i] = BasicItem{ Exchange: f.items[i].exchange, Asset: f.items[i].asset, Currency: f.items[i].currency, @@ -544,7 +543,7 @@ func (f *FundManager) GetAllFunding() []BasicItem { Available: f.items[i].available, Reserved: f.items[i].reserved, USDPrice: usd, - }) + } } return result } @@ -567,8 +566,9 @@ func (f *FundManager) UpdateCollateral(ev common.EventHandler) error { // futures positions aren't collateral, they use it continue } - exch, ok := exchMap[f.items[i].exchange] + _, ok := exchMap[f.items[i].exchange] if !ok { + var exch exchange.IBotExchange exch, err = f.exchangeManager.GetExchangeByName(f.items[i].exchange) if err != nil { return err diff --git a/backtester/funding/spotpair_test.go b/backtester/funding/spotpair_test.go index 3c8ae5a141c..8e9299790c6 100644 --- a/backtester/funding/spotpair_test.go +++ b/backtester/funding/spotpair_test.go @@ -273,8 +273,7 @@ func TestFundReader(t *testing.T) { p := &SpotPair{ base: &Item{exchange: "hello"}, } - ip := p.FundReader() - if ip != p { + if p.FundReader() != p { t.Error("expected the same thing") } } @@ -284,8 +283,7 @@ func TestFundReserver(t *testing.T) { p := &SpotPair{ base: &Item{exchange: "hello"}, } - ip := p.FundReserver() - if ip != p { + if p.FundReserver() != p { t.Error("expected the same thing") } } @@ -295,8 +293,7 @@ func TestFundReleaser(t *testing.T) { p := &SpotPair{ base: &Item{exchange: "hello"}, } - ip := p.FundReleaser() - if ip != p { + if p.FundReleaser() != p { t.Error("expected the same thing") } } @@ -306,10 +303,8 @@ func TestPairReleaser(t *testing.T) { p := &SpotPair{ base: &Item{exchange: "hello"}, } - var expectedError error - _, err := p.PairReleaser() - if !errors.Is(err, expectedError) { - t.Errorf("recevied '%v' expected '%v'", err, expectedError) + if _, err := p.PairReleaser(); !errors.Is(err, nil) { + t.Errorf("recevied '%v' expected '%v'", err, nil) } } @@ -318,10 +313,8 @@ func TestCollateralReleaser(t *testing.T) { p := &SpotPair{ base: &Item{exchange: "hello"}, } - expectedError := ErrNotCollateral - _, err := p.CollateralReleaser() - if !errors.Is(err, expectedError) { - t.Errorf("recevied '%v' expected '%v'", err, expectedError) + if _, err := p.CollateralReleaser(); !errors.Is(err, ErrNotCollateral) { + t.Errorf("recevied '%v' expected '%v'", err, ErrNotCollateral) } } diff --git a/backtester/report/chart.go b/backtester/report/chart.go index 1b42dc37dd0..d1b7547b4de 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -20,12 +20,12 @@ func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.Fun return nil, fmt.Errorf("%w missing funding item statistics", common.ErrNilArguments) } response := &Chart{} - var usdTotalChartPlot []LinePlot + usdTotalChartPlot := make([]LinePlot, len(items)) for i := range items { - usdTotalChartPlot = append(usdTotalChartPlot, LinePlot{ + usdTotalChartPlot[i] = LinePlot{ Value: items[i].Value.InexactFloat64(), UnixMilli: items[i].Time.UTC().UnixMilli(), - }) + } } response.Data = append(response.Data, ChartLine{ Name: "Total USD value", diff --git a/backtester/report/chart_test.go b/backtester/report/chart_test.go index 19e1afc0fdd..a3636010567 100644 --- a/backtester/report/chart_test.go +++ b/backtester/report/chart_test.go @@ -49,10 +49,10 @@ func TestCreateUSDTotalsChart(t *testing.T) { } resp, err := createUSDTotalsChart(items, stats) if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) + t.Fatalf("received '%v' expected '%v'", err, nil) } - if resp == nil { - t.Error("expected not nil") + if len(resp.Data) == 0 { + t.Fatal("expected not nil") } if resp.Data[0].Name != "Total USD value" { t.Error("expected not nil") diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 4b72f913f49..bc956608381 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -1411,6 +1411,10 @@ func TestUpdateExisting(t *testing.T) { od, } s.futuresPositionController = order.SetupPositionController() + err = s.futuresPositionController.TrackNewOrder(od) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } err = s.updateExisting(od) if !errors.Is(err, nil) { t.Errorf("received '%v', expected '%v'", err, nil) diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 81e391d041d..266670fd8e0 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -142,7 +142,6 @@ func (f fExchange) GetFuturesPositions(_ context.Context, a asset.Item, cp curre Price: 1337, Amount: 1337, Fee: 1.337, - FeeAsset: currency.Code{}, Exchange: f.GetName(), ID: "test", Side: order.Long, @@ -2087,6 +2086,7 @@ func TestGetFuturesPositions(t *testing.T) { if err != nil { t.Fatal(err) } + cp.Delimiter = "" b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ @@ -2143,7 +2143,7 @@ func TestGetFuturesPositions(t *testing.T) { Secret: "super wow", }) - r, err := s.GetFuturesPositions(ctx, &gctrpc.GetFuturesPositionsRequest{ + _, err = s.GetFuturesPositions(ctx, &gctrpc.GetFuturesPositionsRequest{ Exchange: fakeExchangeName, Asset: asset.Futures.String(), Pair: &gctrpc.CurrencyPair{ @@ -2153,17 +2153,38 @@ func TestGetFuturesPositions(t *testing.T) { }, Verbose: true, }) + if !errors.Is(err, order.ErrPositionsNotLoadedForExchange) { + t.Fatalf("received '%v', expected '%v'", err, order.ErrPositionsNotLoadedForExchange) + } + + od := &order.Detail{ + Price: 1337, + Amount: 1337, + Fee: 1.337, + Exchange: fakeExchangeName, + ID: "test", + Side: order.Long, + Status: order.Open, + AssetType: asset.Futures, + Date: time.Now(), + Pair: cp, + } + err = s.OrderManager.orderStore.futuresPositionController.TrackNewOrder(od) if !errors.Is(err, nil) { t.Fatalf("received '%v', expected '%v'", err, nil) } - if r == nil { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings - t.Fatal("expected not nil response") - } - if len(r.Positions) != 1 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings - t.Fatal("expected 1 position") - } - if r.TotalOrders != 1 { //nolint:staticcheck,nolintlint // SA5011 Ignore the nil warnings - t.Fatal("expected 1 order") + _, err = s.GetFuturesPositions(ctx, &gctrpc.GetFuturesPositionsRequest{ + Exchange: fakeExchangeName, + Asset: asset.Futures.String(), + Pair: &gctrpc.CurrencyPair{ + Delimiter: currency.DashDelimiter, + Base: cp.Base.String(), + Quote: cp.Quote.String(), + }, + Verbose: true, + }) + if !errors.Is(err, nil) { + t.Fatalf("received '%v', expected '%v'", err, nil) } _, err = s.GetFuturesPositions(ctx, &gctrpc.GetFuturesPositionsRequest{ diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 6745b421afa..bcc028b85ad 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -18,7 +18,7 @@ import ( // to track futures orders func SetupPositionController() *PositionController { return &PositionController{ - positionTrackerControllers: make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker), + multiPositionTrackers: make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker), } } @@ -37,10 +37,10 @@ func (c *PositionController) TrackNewOrder(d *Detail) error { } c.m.Lock() defer c.m.Unlock() - exchM, ok := c.positionTrackerControllers[strings.ToLower(d.Exchange)] + exchM, ok := c.multiPositionTrackers[strings.ToLower(d.Exchange)] if !ok { exchM = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[strings.ToLower(d.Exchange)] = exchM + c.multiPositionTrackers[strings.ToLower(d.Exchange)] = exchM } itemM, ok := exchM[d.AssetType] if !ok { @@ -76,7 +76,7 @@ func (c *PositionController) SetCollateralCurrency(exch string, item asset.Item, return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) } - exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + exchM, ok := c.multiPositionTrackers[strings.ToLower(exch)] if !ok { return fmt.Errorf("cannot set collateral %v for %v %v %v %w", collateralCurrency, exch, item, pair, ErrPositionsNotLoadedForExchange) } @@ -113,7 +113,7 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite if !item.IsFutures() { return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) } - exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + exchM, ok := c.multiPositionTrackers[strings.ToLower(exch)] if !ok { return nil, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) } @@ -142,7 +142,7 @@ func (c *PositionController) UpdateOpenPositionUnrealisedPNL(exch string, item a c.m.Lock() defer c.m.Unlock() - exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + exchM, ok := c.multiPositionTrackers[strings.ToLower(exch)] if !ok { return decimal.Zero, fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) } @@ -284,7 +284,7 @@ func (c *PositionController) ClearPositionsForExchange(exch string, item asset.I if !item.IsFutures() { return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrNotFuturesAsset) } - exchM, ok := c.positionTrackerControllers[strings.ToLower(exch)] + exchM, ok := c.multiPositionTrackers[strings.ToLower(exch)] if !ok { return fmt.Errorf("%v %v %v %w", exch, item, pair, ErrPositionsNotLoadedForExchange) } @@ -320,9 +320,9 @@ func (m *MultiPositionTracker) GetPositions() []PositionStats { } m.m.Lock() defer m.m.Unlock() - resp := make([]PositionStats, len(e.positions)) - for i := range e.positions { - resp[i] = e.positions[i].GetStats() + resp := make([]PositionStats, len(m.positions)) + for i := range m.positions { + resp[i] = m.positions[i].GetStats() } return resp } @@ -812,23 +812,24 @@ func upsertPNLEntry(pnlHistory []PNLResult, entry *PNLResult) ([]PNLResult, erro return nil, errTimeUnset } for i := range pnlHistory { - if entry.Time.Equal(pnlHistory[i].Time) { - pnlHistory[i].UnrealisedPNL = entry.UnrealisedPNL - pnlHistory[i].RealisedPNL = entry.RealisedPNL - pnlHistory[i].RealisedPNLBeforeFees = entry.RealisedPNLBeforeFees - pnlHistory[i].Exposure = entry.Exposure - pnlHistory[i].Direction = entry.Direction - pnlHistory[i].Price = entry.Price - pnlHistory[i].Status = entry.Status - pnlHistory[i].Fee = entry.Fee - if entry.IsOrder { - pnlHistory[i].IsOrder = true - } - if entry.IsLiquidated { - pnlHistory[i].IsLiquidated = true - } - return pnlHistory, nil + if !entry.Time.Equal(pnlHistory[i].Time) { + continue + } + pnlHistory[i].UnrealisedPNL = entry.UnrealisedPNL + pnlHistory[i].RealisedPNL = entry.RealisedPNL + pnlHistory[i].RealisedPNLBeforeFees = entry.RealisedPNLBeforeFees + pnlHistory[i].Exposure = entry.Exposure + pnlHistory[i].Direction = entry.Direction + pnlHistory[i].Price = entry.Price + pnlHistory[i].Status = entry.Status + pnlHistory[i].Fee = entry.Fee + if entry.IsOrder { + pnlHistory[i].IsOrder = true + } + if entry.IsLiquidated { + pnlHistory[i].IsLiquidated = true } + return pnlHistory, nil } pnlHistory = append(pnlHistory, *entry) sort.Slice(pnlHistory, func(i, j int) bool { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 36245a0ce87..89f3a4dcb46 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -369,7 +369,7 @@ func TestExchangeTrackNewOrder(t *testing.T) { func TestSetupPositionControllerReal(t *testing.T) { t.Parallel() pc := SetupPositionController() - if pc.positionTrackerControllers == nil { + if pc.multiPositionTrackers == nil { t.Error("unexpected nil") } } @@ -499,14 +499,14 @@ func TestGetPositionsForExchange(t *testing.T) { if len(pos) != 0 { t.Error("expected zero") } - c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[testExchange] = nil + c.multiPositionTrackers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.multiPositionTrackers[testExchange] = nil _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForAsset) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - c.positionTrackerControllers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[testExchange][asset.Futures] = nil + c.multiPositionTrackers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.multiPositionTrackers[testExchange][asset.Futures] = nil _, err = c.GetPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForPair) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) @@ -516,8 +516,8 @@ func TestGetPositionsForExchange(t *testing.T) { t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) } - c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + c.multiPositionTrackers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.multiPositionTrackers[testExchange][asset.Futures][p] = &MultiPositionTracker{ exchange: testExchange, } @@ -528,7 +528,7 @@ func TestGetPositionsForExchange(t *testing.T) { if len(pos) != 0 { t.Fatal("expected zero") } - c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + c.multiPositionTrackers[testExchange][asset.Futures][p] = &MultiPositionTracker{ exchange: testExchange, positions: []*PositionTracker{ { @@ -561,14 +561,14 @@ func TestClearPositionsForExchange(t *testing.T) { if !errors.Is(err, ErrPositionsNotLoadedForExchange) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - c.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[testExchange] = nil + c.multiPositionTrackers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.multiPositionTrackers[testExchange] = nil err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForAsset) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForExchange) } - c.positionTrackerControllers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[testExchange][asset.Futures] = nil + c.multiPositionTrackers[testExchange] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + c.multiPositionTrackers[testExchange][asset.Futures] = nil err = c.ClearPositionsForExchange(testExchange, asset.Futures, p) if !errors.Is(err, ErrPositionsNotLoadedForPair) { t.Errorf("received '%v' expected '%v", err, ErrPositionsNotLoadedForPair) @@ -578,11 +578,11 @@ func TestClearPositionsForExchange(t *testing.T) { t.Errorf("received '%v' expected '%v", err, ErrNotFuturesAsset) } - c.positionTrackerControllers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) - c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + c.multiPositionTrackers[testExchange][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + c.multiPositionTrackers[testExchange][asset.Futures][p] = &MultiPositionTracker{ exchange: testExchange, } - c.positionTrackerControllers[testExchange][asset.Futures][p] = &MultiPositionTracker{ + c.multiPositionTrackers[testExchange][asset.Futures][p] = &MultiPositionTracker{ exchange: testExchange, underlying: currency.DOGE, positions: []*PositionTracker{ @@ -595,7 +595,7 @@ func TestClearPositionsForExchange(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v", err, nil) } - if len(c.positionTrackerControllers[testExchange][asset.Futures][p].positions) != 0 { + if len(c.multiPositionTrackers[testExchange][asset.Futures][p].positions) != 0 { t.Fatal("expected 0") } c = nil @@ -825,34 +825,34 @@ func TestSetCollateralCurrency(t *testing.T) { t.Errorf("received '%v' expected '%v", err, expectedError) } cp := currency.NewPair(currency.BTC, currency.USDT) - pc.positionTrackerControllers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) + pc.multiPositionTrackers = make(map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker) err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) expectedError = ErrPositionsNotLoadedForExchange if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } - pc.positionTrackerControllers["hi"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) + pc.multiPositionTrackers["hi"] = make(map[asset.Item]map[currency.Pair]*MultiPositionTracker) err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) expectedError = ErrPositionsNotLoadedForAsset if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } - pc.positionTrackerControllers["hi"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) + pc.multiPositionTrackers["hi"][asset.Futures] = make(map[currency.Pair]*MultiPositionTracker) err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) expectedError = ErrPositionsNotLoadedForPair if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } - pc.positionTrackerControllers["hi"][asset.Futures][cp] = nil + pc.multiPositionTrackers["hi"][asset.Futures][cp] = nil err = pc.SetCollateralCurrency("hi", asset.Futures, cp, currency.DOGE) expectedError = common.ErrNilPointer if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } - pc.positionTrackerControllers["hi"][asset.Futures][cp] = &MultiPositionTracker{ + pc.multiPositionTrackers["hi"][asset.Futures][cp] = &MultiPositionTracker{ exchange: "hi", asset: asset.Futures, pair: cp, @@ -878,12 +878,12 @@ func TestSetCollateralCurrency(t *testing.T) { t.Fatalf("received '%v' expected '%v", err, expectedError) } - if !pc.positionTrackerControllers["hi"][asset.Futures][cp].collateralCurrency.Equal(currency.DOGE) { - t.Errorf("received '%v' expected '%v'", pc.positionTrackerControllers["hi"][asset.Futures][cp].collateralCurrency, currency.DOGE) + if !pc.multiPositionTrackers["hi"][asset.Futures][cp].collateralCurrency.Equal(currency.DOGE) { + t.Errorf("received '%v' expected '%v'", pc.multiPositionTrackers["hi"][asset.Futures][cp].collateralCurrency, currency.DOGE) } - if !pc.positionTrackerControllers["hi"][asset.Futures][cp].positions[0].collateralCurrency.Equal(currency.DOGE) { - t.Errorf("received '%v' expected '%v'", pc.positionTrackerControllers["hi"][asset.Futures][cp].positions[0].collateralCurrency, currency.DOGE) + if !pc.multiPositionTrackers["hi"][asset.Futures][cp].positions[0].collateralCurrency.Equal(currency.DOGE) { + t.Errorf("received '%v' expected '%v'", pc.multiPositionTrackers["hi"][asset.Futures][cp].positions[0].collateralCurrency, currency.DOGE) } pc = nil @@ -913,7 +913,7 @@ func TestMPTUpdateOpenPositionUnrealisedPNL(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } - result, err := pc.positionTrackerControllers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) + result, err := pc.multiPositionTrackers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } @@ -922,15 +922,15 @@ func TestMPTUpdateOpenPositionUnrealisedPNL(t *testing.T) { } expectedError = ErrPositionClosed - pc.positionTrackerControllers["hi"][asset.Futures][cp].positions[0].status = Closed - _, err = pc.positionTrackerControllers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) + pc.multiPositionTrackers["hi"][asset.Futures][cp].positions[0].status = Closed + _, err = pc.multiPositionTrackers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } expectedError = ErrPositionsNotLoadedForPair - pc.positionTrackerControllers["hi"][asset.Futures][cp].positions = nil - _, err = pc.positionTrackerControllers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) + pc.multiPositionTrackers["hi"][asset.Futures][cp].positions = nil + _, err = pc.multiPositionTrackers["hi"][asset.Futures][cp].UpdateOpenPositionUnrealisedPNL(1337, time.Now()) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v", err, expectedError) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index f113fafdd8b..c7114e0e28b 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -127,8 +127,8 @@ type UsedCollateralBreakdown struct { // and so all you need to do is send all orders to // the position controller and its all tracked happily type PositionController struct { - m sync.Mutex - positionTrackerControllers map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker + m sync.Mutex + multiPositionTrackers map[string]map[asset.Item]map[currency.Pair]*MultiPositionTracker } // MultiPositionTracker will track the performance of @@ -272,7 +272,7 @@ type PNLResult struct { Direction Side Fee decimal.Decimal IsLiquidated bool - // Is event is supposed to show that something has happend and it isnt just tracking in time + // Is event is supposed to show that something has happened and it isnt just tracking in time IsOrder bool } From ae3005ad1e633e55e4992d64a199eb0e08be9f3d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 16:59:56 +1000 Subject: [PATCH 127/171] defaults to non-coloured rendering --- backtester/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtester/main.go b/backtester/main.go index 082d7d47be3..6178abf70ca 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -71,7 +71,7 @@ func main() { flag.BoolVar( &colourOutput, "colouroutput", - true, + false, "if enabled, will print in colours, if your terminal supports \033[38;5;99m[colours like this]\u001b[0m") flag.BoolVar( &logSubHeader, From c716b4e6e42bbd356c99a2877119614c13705f80 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 17:19:09 +1000 Subject: [PATCH 128/171] Chart rendering --- backtester/report/report.go | 17 ++-- backtester/report/tpl.gohtml | 166 ++++++++++++++++++----------------- 2 files changed, 93 insertions(+), 90 deletions(-) diff --git a/backtester/report/report.go b/backtester/report/report.go index c567e93d453..1170ce19aa9 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -56,15 +56,16 @@ func (d *Data) GenerateReport() error { } } - d.PNLOverTimeChart, err = createPNLCharts(d.Statistics.ExchangeAssetPairStatistics) - if err != nil { - return err - } - d.FuturesSpotDiffChart, err = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) - if err != nil { - return err + if d.Statistics.HasCollateral { + d.PNLOverTimeChart, err = createPNLCharts(d.Statistics.ExchangeAssetPairStatistics) + if err != nil { + return err + } + d.FuturesSpotDiffChart, err = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) + if err != nil { + return err + } } - tmpl := template.Must( template.ParseFiles(d.TemplatePath), ) diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 8f3842bd13b..610485373e4 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -779,8 +779,9 @@ // Apply the theme Highcharts.setOptions(Highcharts.theme); + {{ if .PNLOverTimeChart }}

PNL Over Time

-
+
- -

Futures Spot Diff %

-
- -
+ }] + } + }); + +
+ {{end}} {{ if eq $.Config.StrategySettings.DisableUSDTracking false }}

USD Totals

@@ -1522,28 +1524,28 @@ {{ if .ReportItem.IsCollateral}} {{ else }} {{ if .ReportItem.Currency.IsFiatCurrency}} - {{else}} - - Starting Close Price - {{$.Prettify.Decimal8 .StartingClosePrice.Value}} at {{.StartingClosePrice.Time}} - - - Ending Close Price - {{$.Prettify.Decimal8 .EndingClosePrice.Value}} at {{.EndingClosePrice.Time}} - - - Highest Close Price - {{$.Prettify.Decimal8 .HighestClosePrice.Value}} at {{.HighestClosePrice.Time}} - - - Lowest Close Price - {{$.Prettify.Decimal8 .LowestClosePrice.Value}} at {{.LowestClosePrice.Time}} - - - Market Movement - {{$.Prettify.Decimal8 .MarketMovement}} - - {{end }} + {{else}} + + Starting Close Price + {{$.Prettify.Decimal8 .StartingClosePrice.Value}} at {{.StartingClosePrice.Time}} + + + Ending Close Price + {{$.Prettify.Decimal8 .EndingClosePrice.Value}} at {{.EndingClosePrice.Time}} + + + Highest Close Price + {{$.Prettify.Decimal8 .HighestClosePrice.Value}} at {{.HighestClosePrice.Time}} + + + Lowest Close Price + {{$.Prettify.Decimal8 .LowestClosePrice.Value}} at {{.LowestClosePrice.Time}} + + + Market Movement + {{$.Prettify.Decimal8 .MarketMovement}} + + {{end }} Strategy Movement {{ if .ReportItem.ShowInfinite}} From 61d175aad5f0cf241cd187ea780ba8df78fce3a7 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 20 Apr 2022 17:30:16 +1000 Subject: [PATCH 129/171] Fixes surprise non-local-lints --- backtester/engine/backtest_test.go | 3 +- .../eventhandlers/portfolio/portfolio_test.go | 10 +++---- backtester/eventtypes/order/order_test.go | 30 ++++++++----------- backtester/funding/spotpair_test.go | 6 ++-- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index b8d1b05ed7f..6acdb237fd0 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -1067,8 +1067,7 @@ func TestProcessOrderEvent(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, expectedError) } ev2 := bt.EventQueue.NextEvent() - _, ok := ev2.(fill.Event) - if !ok { + if _, ok := ev2.(fill.Event); !ok { t.Fatal("expected fill event") } } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 9769dd1236e..22fbf5e7df3 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -1179,9 +1179,8 @@ func TestGetExposure(t *testing.T) { IsLiquidated: true, }, } - result := p.GetExposure() - if !result.Equal(p.Result.Exposure) { - t.Errorf("received '%v' expected '%v'", result, p.Result.Exposure) + if !p.GetExposure().Equal(p.Result.Exposure) { + t.Errorf("received '%v' expected '%v'", p.GetExposure(), p.Result.Exposure) } } @@ -1231,9 +1230,8 @@ func TestGetDirection(t *testing.T) { IsLiquidated: true, }, } - result := p.GetDirection() - if result != (p.Result.Direction) { - t.Errorf("received '%v' expected '%v'", result, p.Result.Direction) + if p.GetDirection() != (p.Result.Direction) { + t.Errorf("received '%v' expected '%v'", p.GetDirection(), p.Result.Direction) } } diff --git a/backtester/eventtypes/order/order_test.go b/backtester/eventtypes/order/order_test.go index 56978b3c02f..705b8956dcb 100644 --- a/backtester/eventtypes/order/order_test.go +++ b/backtester/eventtypes/order/order_test.go @@ -113,9 +113,8 @@ func TestGetBuyLimit(t *testing.T) { k := Order{ BuyLimit: decimal.NewFromInt(1337), } - bl := k.GetBuyLimit() - if !bl.Equal(decimal.NewFromInt(1337)) { - t.Errorf("received '%v' expected '%v'", bl, decimal.NewFromInt(1337)) + if !k.GetBuyLimit().Equal(decimal.NewFromInt(1337)) { + t.Errorf("received '%v' expected '%v'", k.GetBuyLimit(), decimal.NewFromInt(1337)) } } @@ -124,9 +123,8 @@ func TestGetSellLimit(t *testing.T) { k := Order{ SellLimit: decimal.NewFromInt(1337), } - sl := k.GetSellLimit() - if !sl.Equal(decimal.NewFromInt(1337)) { - t.Errorf("received '%v' expected '%v'", sl, decimal.NewFromInt(1337)) + if !k.GetSellLimit().Equal(decimal.NewFromInt(1337)) { + t.Errorf("received '%v' expected '%v'", k.GetSellLimit(), decimal.NewFromInt(1337)) } } @@ -138,9 +136,8 @@ func TestPair(t *testing.T) { CurrencyPair: cp, }, } - p := k.Pair() - if !p.Equal(cp) { - t.Errorf("received '%v' expected '%v'", p, cp) + if !k.Pair().Equal(cp) { + t.Errorf("received '%v' expected '%v'", k.Pair(), cp) } } @@ -149,9 +146,8 @@ func TestGetStatus(t *testing.T) { k := Order{ Status: gctorder.UnknownStatus, } - s := k.GetStatus() - if s != gctorder.UnknownStatus { - t.Errorf("received '%v' expected '%v'", s, gctorder.UnknownStatus) + if k.GetStatus() != gctorder.UnknownStatus { + t.Errorf("received '%v' expected '%v'", k.GetStatus(), gctorder.UnknownStatus) } } @@ -160,9 +156,8 @@ func TestGetFillDependentEvent(t *testing.T) { k := Order{ FillDependentEvent: &signal.Signal{Amount: decimal.NewFromInt(1337)}, } - fde := k.GetFillDependentEvent() - if !fde.GetAmount().Equal(decimal.NewFromInt(1337)) { - t.Errorf("received '%v' expected '%v'", fde, decimal.NewFromInt(1337)) + if !k.GetFillDependentEvent().GetAmount().Equal(decimal.NewFromInt(1337)) { + t.Errorf("received '%v' expected '%v'", k.GetFillDependentEvent(), decimal.NewFromInt(1337)) } } func TestIsClosingPosition(t *testing.T) { @@ -170,8 +165,7 @@ func TestIsClosingPosition(t *testing.T) { k := Order{ ClosingPosition: true, } - s := k.IsClosingPosition() - if !s { - t.Errorf("received '%v' expected '%v'", s, true) + if !k.IsClosingPosition() { + t.Errorf("received '%v' expected '%v'", k.IsClosingPosition(), true) } } diff --git a/backtester/funding/spotpair_test.go b/backtester/funding/spotpair_test.go index 8e9299790c6..b54faecb3fa 100644 --- a/backtester/funding/spotpair_test.go +++ b/backtester/funding/spotpair_test.go @@ -261,10 +261,8 @@ func TestGetCollateralReader(t *testing.T) { p := &SpotPair{ base: &Item{exchange: "hello"}, } - expectedError := ErrNotCollateral - _, err := p.GetCollateralReader() - if !errors.Is(err, expectedError) { - t.Errorf("recevied '%v' expected '%v'", err, expectedError) + if _, err := p.GetCollateralReader(); !errors.Is(err, ErrNotCollateral) { + t.Errorf("recevied '%v' expected '%v'", err, ErrNotCollateral) } } From be9bbf67f81004793fbba1f14a92a4dc89c97724 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 28 Apr 2022 13:31:11 +1000 Subject: [PATCH 130/171] Niterinos to the extremeos --- backtester/common/common.go | 55 ++ backtester/common/common_types.go | 115 +-- backtester/config/config.go | 111 ++- backtester/config/config_test.go | 833 ++++++++++-------- backtester/config/config_types.go | 33 +- backtester/data/data.go | 3 + backtester/data/data_test.go | 98 +-- backtester/data/kline/csv/csv.go | 2 +- backtester/data/kline/database/database.go | 2 +- backtester/data/kline/kline.go | 16 +- backtester/engine/backtest.go | 71 +- backtester/engine/backtest_test.go | 84 +- backtester/engine/live.go | 18 +- backtester/engine/setup.go | 91 +- .../eventhandlers/portfolio/portfolio.go | 152 ++-- .../eventhandlers/portfolio/portfolio_test.go | 5 +- .../portfolio/portfolio_types.go | 1 + backtester/eventhandlers/portfolio/setup.go | 14 +- backtester/eventhandlers/statistics/common.go | 11 +- .../statistics/currencystatistics.go | 6 +- .../eventhandlers/statistics/printresults.go | 282 +++--- .../eventhandlers/statistics/statistics.go | 4 +- .../statistics/statistics_test.go | 21 +- .../trackingcurrencies/trackingcurrencies.go | 21 +- backtester/main.go | 23 +- backtester/report/report.go | 6 +- backtester/report/report_test.go | 18 +- backtester/report/tpl.gohtml | 2 +- currency/manager.go | 2 +- log/sublogger.go | 3 +- 30 files changed, 1072 insertions(+), 1031 deletions(-) diff --git a/backtester/common/common.go b/backtester/common/common.go index 9b07a2f8ecf..946448c7539 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -3,6 +3,8 @@ package common import ( "fmt" "strings" + + "github.com/thrasher-corp/gocryptotrader/log" ) // DataTypeToInt converts the config string value into an int @@ -50,3 +52,56 @@ func FitStringToLimit(str, spacer string, limit int, upper bool) string { return str[0:limit] } + +// RegisterBacktesterSubLoggers sets up all custom Backtester sub-loggers +func RegisterBacktesterSubLoggers() error { + var err error + Backtester, err = log.NewSubLogger("Backtester") + if err != nil { + return err + } + Setup, err = log.NewSubLogger("Setup") + if err != nil { + return err + } + Strategy, err = log.NewSubLogger("Strategy") + if err != nil { + return err + } + Report, err = log.NewSubLogger("Report") + if err != nil { + return err + } + Statistics, err = log.NewSubLogger("Statistics") + if err != nil { + return err + } + CurrencyStatistics, err = log.NewSubLogger("CurrencyStatistics") + if err != nil { + return err + } + FundingStatistics, err = log.NewSubLogger("FundingStatistics") + if err != nil { + return err + } + Backtester, err = log.NewSubLogger("Sizing") + if err != nil { + return err + } + Holdings, err = log.NewSubLogger("Holdings") + if err != nil { + return err + } + Data, err = log.NewSubLogger("Data") + if err != nil { + return err + } + + // Set to existing registered sub-loggers + Config = log.ConfigMgr + Portfolio = log.PortfolioMgr + Exchange = log.ExchangeSys + Fill = log.Fill + + return nil +} diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 7cb54ed5425..0feef861202 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -80,50 +80,21 @@ type EventHandler interface { AppendReasonf(string, ...interface{}) } -// List of all the custom sublogger names -const ( - Backtester = "Backtester" - Setup = "Setup" - Strategy = "Strategy" - Config = "Config" - Portfolio = "Portfolio" - Exchange = "Exchange" - Fill = "Fill" - Funding = "Funding" - Report = "Report" - Statistics = "Statistics" - CurrencyStatistics = "CurrencyStatistics" - FundingStatistics = "FundingStatistics" - Compliance = "Compliance" - Sizing = "Sizing" - Risk = "Risk" - Holdings = "Holdings" - Slippage = "Slippage" - Data = "Data" -) - +// custom subloggers for backtester use var ( - // SubLoggers is a map of loggers to use across the backtester - SubLoggers = map[string]*log.SubLogger{ - Backtester: nil, - Setup: nil, - Strategy: nil, - Config: nil, - Portfolio: nil, - Exchange: nil, - Fill: nil, - Funding: nil, - Report: nil, - Statistics: nil, - CurrencyStatistics: nil, - FundingStatistics: nil, - Compliance: nil, - Sizing: nil, - Risk: nil, - Holdings: nil, - Slippage: nil, - Data: nil, - } + Backtester *log.SubLogger + Setup *log.SubLogger + Strategy *log.SubLogger + Config *log.SubLogger + Portfolio *log.SubLogger + Exchange *log.SubLogger + Fill *log.SubLogger + Report *log.SubLogger + Statistics *log.SubLogger + CurrencyStatistics *log.SubLogger + FundingStatistics *log.SubLogger + Holdings *log.SubLogger + Data *log.SubLogger ) // DataEventHandler interface used for loading and interacting with Data @@ -157,50 +128,30 @@ var ( ColourInfo = "\u001B[32m" ColourDebug = "\u001B[34m" ColourWarn = "\u001B[33m" - ColourProblem = "\033[38;5;251m" ColourError = "\033[38;5;196m" ) -// logo and colour storage var ( - logo01 = " " - logo02 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@ " - logo03 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + ",,,,,," + ColourWhite + " " - logo04 = " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,,,,,," + ColourWhite + " " - logo05 = " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " " - logo06 = " " + ColourWhite + "@@@@@@" + ColourGrey + "(,,,,,,,, " + ColourGrey + ",," + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " " - logo07 = " " + ColourGrey + ",," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, #,,,,,,,,,,,,,,,,,," + ColourWhite + " " - logo08 = " " + ColourGrey + ",,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,,,,,,,,,,,,,," + ColourGreen + "%%%%%%%" + ColourWhite + " " - logo09 = " " + ColourGrey + ",,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,," + ColourGreen + "%%%%%" + ColourGrey + " ,,,,,," + ColourGrey + "%" + ColourGreen + "%%%%%%" + ColourWhite + " " - logo10 = " " + ColourGrey + ",,,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,," + ColourGreen + "%%%%%%%%%%%%%%%%%%" + ColourGrey + "#" + ColourGreen + "%%" + ColourGrey + " " - logo11 = " " + ColourGrey + ",,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,," + ColourGreen + "%%%" + ColourGrey + " ,,,,," + ColourGreen + "%%%%%%%%" + ColourGrey + ",,,,, " - logo12 = " " + ColourGrey + ",,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourGreen + "%%" + ColourGrey + ",, ,,,,,,," + ColourWhite + "@" + ColourGreen + "*%%," + ColourWhite + "@" + ColourGrey + ",,,,,, " - logo13 = " " + ColourGrey + "*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourGrey + ",,,,," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " " - logo14 = " " + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " " - logo15 = " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " " - logo16 = " " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + "#,,,,,,," + ColourWhite + " " - logo17 = " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + "*,,,," + ColourWhite + " " - logo18 = " " + ColourWhite + "@@@@@@@@@@@@@@@@" + ColourDefault + " " - + // LogoLines contains the lovely GCT logo LogoLines = []string{ - logo01, - logo02, - logo03, - logo04, - logo05, - logo06, - logo07, - logo08, - logo09, - logo10, - logo11, - logo12, - logo13, - logo14, - logo15, - logo16, - logo17, - logo18, + " ", + " " + ColourWhite + "@@@@@@@@@@@@@@@@@ ", + " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + ",,,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,,,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@" + ColourGrey + "(,,,,,,,, " + ColourGrey + ",," + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " ", + " " + ColourGrey + ",," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, #,,,,,,,,,,,,,,,,,," + ColourWhite + " ", + " " + ColourGrey + ",,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,,,,,,,,,,,,,," + ColourGreen + "%%%%%%%" + ColourWhite + " ", + " " + ColourGrey + ",,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,," + ColourGreen + "%%%%%" + ColourGrey + " ,,,,,," + ColourGrey + "%" + ColourGreen + "%%%%%%" + ColourWhite + " ", + " " + ColourGrey + ",,,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,," + ColourGreen + "%%%%%%%%%%%%%%%%%%" + ColourGrey + "#" + ColourGreen + "%%" + ColourGrey + " ", + " " + ColourGrey + ",,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,," + ColourGreen + "%%%" + ColourGrey + " ,,,,," + ColourGreen + "%%%%%%%%" + ColourGrey + ",,,,, ", + " " + ColourGrey + ",,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourGreen + "%%" + ColourGrey + ",, ,,,,,,," + ColourWhite + "@" + ColourGreen + "*%%," + ColourWhite + "@" + ColourGrey + ",,,,,, ", + " " + ColourGrey + "*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourGrey + ",,,,," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " , ", + " " + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + "#,,,,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + "*,,,," + ColourWhite + " ", + " " + ColourWhite + "@@@@@@@@@@@@@@@@" + ColourDefault + " ", } ) diff --git a/backtester/config/config.go b/backtester/config/config.go index 8e5e15cc8fc..bdd6e9f432d 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -165,14 +165,13 @@ func (c *Config) validateCurrencySettings() error { return errNoCurrencySettings } for i := range c.CurrencySettings { - if c.CurrencySettings[i].Asset == asset.PerpetualSwap.String() || - c.CurrencySettings[i].Asset == asset.PerpetualContract.String() { + if c.CurrencySettings[i].Asset == asset.PerpetualSwap || + c.CurrencySettings[i].Asset == asset.PerpetualContract { return errPerpetualsUnsupported } - if c.CurrencySettings[i].FuturesDetails != nil { - if c.CurrencySettings[i].Quote == "PERP" || c.CurrencySettings[i].Base == "PI" { - return errPerpetualsUnsupported - } + if c.CurrencySettings[i].Asset == asset.Futures && + (c.CurrencySettings[i].Quote.String() == "PERP" || c.CurrencySettings[i].Base.String() == "PI") { + return errPerpetualsUnsupported } if c.CurrencySettings[i].SpotDetails != nil { if c.FundingSettings.UseExchangeLevelFunding { @@ -203,11 +202,11 @@ func (c *Config) validateCurrencySettings() error { } } } - if c.CurrencySettings[i].Base == "" { + if c.CurrencySettings[i].Base.IsEmpty() { return errUnsetCurrency } - if c.CurrencySettings[i].Asset == "" { - return errUnsetAsset + if !c.CurrencySettings[i].Asset.IsValid() { + return fmt.Errorf("%v %w", c.CurrencySettings[i].Asset, asset.ErrNotSupported) } if c.CurrencySettings[i].ExchangeName == "" { return errUnsetExchange @@ -224,25 +223,25 @@ func (c *Config) validateCurrencySettings() error { // PrintSetting prints relevant settings to the console for easy reading func (c *Config) PrintSetting() { - log.Info(common.SubLoggers[common.Config], common.ColourH1+"------------------Backtester Settings------------------------"+common.ColourDefault) - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Strategy Settings--------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Strategy: %s", c.StrategySettings.Name) + log.Info(common.Config, common.ColourH1+"------------------Backtester Settings------------------------"+common.ColourDefault) + log.Info(common.Config, common.ColourH2+"------------------Strategy Settings--------------------------"+common.ColourDefault) + log.Infof(common.Config, "Strategy: %s", c.StrategySettings.Name) if len(c.StrategySettings.CustomSettings) > 0 { - log.Info(common.SubLoggers[common.Config], "Custom strategy variables:") + log.Info(common.Config, "Custom strategy variables:") for k, v := range c.StrategySettings.CustomSettings { - log.Infof(common.SubLoggers[common.Config], "%s: %v", k, v) + log.Infof(common.Config, "%s: %v", k, v) } } else { - log.Info(common.SubLoggers[common.Config], "Custom strategy variables: unset") + log.Info(common.Config, "Custom strategy variables: unset") } - log.Infof(common.SubLoggers[common.Config], "Simultaneous Signal Processing: %v", c.StrategySettings.SimultaneousSignalProcessing) - log.Infof(common.SubLoggers[common.Config], "USD value tracking: %v", !c.StrategySettings.DisableUSDTracking) + log.Infof(common.Config, "Simultaneous Signal Processing: %v", c.StrategySettings.SimultaneousSignalProcessing) + log.Infof(common.Config, "USD value tracking: %v", !c.StrategySettings.DisableUSDTracking) if c.FundingSettings.UseExchangeLevelFunding && c.StrategySettings.SimultaneousSignalProcessing { - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Funding Settings---------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Use Exchange Level Funding: %v", c.FundingSettings.UseExchangeLevelFunding) + log.Info(common.Config, common.ColourH2+"------------------Funding Settings---------------------------"+common.ColourDefault) + log.Infof(common.Config, "Use Exchange Level Funding: %v", c.FundingSettings.UseExchangeLevelFunding) for i := range c.FundingSettings.ExchangeLevelFunding { - log.Infof(common.SubLoggers[common.Config], "Initial funds for %v %v %v: %v", + log.Infof(common.Config, "Initial funds for %v %v %v: %v", c.FundingSettings.ExchangeLevelFunding[i].ExchangeName, c.FundingSettings.ExchangeLevelFunding[i].Asset, c.FundingSettings.ExchangeLevelFunding[i].Currency, @@ -255,65 +254,65 @@ func (c *Config) PrintSetting() { c.CurrencySettings[i].Asset, c.CurrencySettings[i].Base, c.CurrencySettings[i].Quote) - log.Infof(common.SubLoggers[common.Config], currStr[:61]) - log.Infof(common.SubLoggers[common.Config], "Exchange: %v", c.CurrencySettings[i].ExchangeName) + log.Infof(common.Config, currStr[:61]) + log.Infof(common.Config, "Exchange: %v", c.CurrencySettings[i].ExchangeName) if !c.FundingSettings.UseExchangeLevelFunding && c.CurrencySettings[i].SpotDetails != nil { if c.CurrencySettings[i].SpotDetails.InitialBaseFunds != nil { - log.Infof(common.SubLoggers[common.Config], "Initial base funds: %v %v", + log.Infof(common.Config, "Initial base funds: %v %v", c.CurrencySettings[i].SpotDetails.InitialBaseFunds.Round(8), c.CurrencySettings[i].Base) } if c.CurrencySettings[i].SpotDetails.InitialQuoteFunds != nil { - log.Infof(common.SubLoggers[common.Config], "Initial quote funds: %v %v", + log.Infof(common.Config, "Initial quote funds: %v %v", c.CurrencySettings[i].SpotDetails.InitialQuoteFunds.Round(8), c.CurrencySettings[i].Quote) } } if c.CurrencySettings[i].TakerFee != nil { - log.Infof(common.SubLoggers[common.Config], "Maker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) + log.Infof(common.Config, "Taker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) } if c.CurrencySettings[i].MakerFee != nil { - log.Infof(common.SubLoggers[common.Config], "Taker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) + log.Infof(common.Config, "Maker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) } - log.Infof(common.SubLoggers[common.Config], "Minimum slippage percent %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) - log.Infof(common.SubLoggers[common.Config], "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) - log.Infof(common.SubLoggers[common.Config], "Buy rules: %+v", c.CurrencySettings[i].BuySide) - log.Infof(common.SubLoggers[common.Config], "Sell rules: %+v", c.CurrencySettings[i].SellSide) - if c.CurrencySettings[i].FuturesDetails != nil && c.CurrencySettings[i].Asset == asset.Futures.String() { - log.Infof(common.SubLoggers[common.Config], "Leverage rules: %+v", c.CurrencySettings[i].FuturesDetails.Leverage) + log.Infof(common.Config, "Minimum slippage percent %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) + log.Infof(common.Config, "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) + log.Infof(common.Config, "Buy rules: %+v", c.CurrencySettings[i].BuySide) + log.Infof(common.Config, "Sell rules: %+v", c.CurrencySettings[i].SellSide) + if c.CurrencySettings[i].FuturesDetails != nil && c.CurrencySettings[i].Asset == asset.Futures { + log.Infof(common.Config, "Leverage rules: %+v", c.CurrencySettings[i].FuturesDetails.Leverage) } - log.Infof(common.SubLoggers[common.Config], "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) + log.Infof(common.Config, "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits) } - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Portfolio Settings-------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Buy rules: %+v", c.PortfolioSettings.BuySide) - log.Infof(common.SubLoggers[common.Config], "Sell rules: %+v", c.PortfolioSettings.SellSide) - log.Infof(common.SubLoggers[common.Config], "Leverage rules: %+v", c.PortfolioSettings.Leverage) + log.Info(common.Config, common.ColourH2+"------------------Portfolio Settings-------------------------"+common.ColourDefault) + log.Infof(common.Config, "Buy rules: %+v", c.PortfolioSettings.BuySide) + log.Infof(common.Config, "Sell rules: %+v", c.PortfolioSettings.SellSide) + log.Infof(common.Config, "Leverage rules: %+v", c.PortfolioSettings.Leverage) if c.DataSettings.LiveData != nil { - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Live Settings------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) - log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) - log.Infof(common.SubLoggers[common.Config], "REAL ORDERS: %v", c.DataSettings.LiveData.RealOrders) - log.Infof(common.SubLoggers[common.Config], "Overriding GCT API settings: %v", c.DataSettings.LiveData.APIClientIDOverride != "") + log.Info(common.Config, common.ColourH2+"------------------Live Settings------------------------------"+common.ColourDefault) + log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType) + log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval) + log.Infof(common.Config, "REAL ORDERS: %v", c.DataSettings.LiveData.RealOrders) + log.Infof(common.Config, "Overriding GCT API settings: %v", c.DataSettings.LiveData.APIClientIDOverride != "") } if c.DataSettings.APIData != nil { - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------API Settings-------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) - log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) - log.Infof(common.SubLoggers[common.Config], "Start date: %v", c.DataSettings.APIData.StartDate.Format(gctcommon.SimpleTimeFormat)) - log.Infof(common.SubLoggers[common.Config], "End date: %v", c.DataSettings.APIData.EndDate.Format(gctcommon.SimpleTimeFormat)) + log.Info(common.Config, common.ColourH2+"------------------API Settings-------------------------------"+common.ColourDefault) + log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType) + log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval) + log.Infof(common.Config, "Start date: %v", c.DataSettings.APIData.StartDate.Format(gctcommon.SimpleTimeFormat)) + log.Infof(common.Config, "End date: %v", c.DataSettings.APIData.EndDate.Format(gctcommon.SimpleTimeFormat)) } if c.DataSettings.CSVData != nil { - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------CSV Settings-------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) - log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) - log.Infof(common.SubLoggers[common.Config], "CSV file: %v", c.DataSettings.CSVData.FullPath) + log.Info(common.Config, common.ColourH2+"------------------CSV Settings-------------------------------"+common.ColourDefault) + log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType) + log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval) + log.Infof(common.Config, "CSV file: %v", c.DataSettings.CSVData.FullPath) } if c.DataSettings.DatabaseData != nil { - log.Info(common.SubLoggers[common.Config], common.ColourH2+"------------------Database Settings--------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Config], "Data type: %v", c.DataSettings.DataType) - log.Infof(common.SubLoggers[common.Config], "Interval: %v", c.DataSettings.Interval) - log.Infof(common.SubLoggers[common.Config], "Start date: %v", c.DataSettings.DatabaseData.StartDate.Format(gctcommon.SimpleTimeFormat)) - log.Infof(common.SubLoggers[common.Config], "End date: %v", c.DataSettings.DatabaseData.EndDate.Format(gctcommon.SimpleTimeFormat)) + log.Info(common.Config, common.ColourH2+"------------------Database Settings--------------------------"+common.ColourDefault) + log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType) + log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval) + log.Infof(common.Config, "Start date: %v", c.DataSettings.DatabaseData.StartDate.Format(gctcommon.SimpleTimeFormat)) + log.Infof(common.Config, "End date: %v", c.DataSettings.DatabaseData.EndDate.Format(gctcommon.SimpleTimeFormat)) } } diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 0e4c7344cee..3a6a14e9442 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -55,43 +55,300 @@ func TestMain(m *testing.M) { } func TestLoadConfig(t *testing.T) { + t.Parallel() _, err := LoadConfig([]byte(`{}`)) if err != nil { t.Error(err) } } -func TestReadConfigFromFile(t *testing.T) { - tempDir, err := ioutil.TempDir("", "") +func TestValidateDate(t *testing.T) { + t.Parallel() + c := Config{} + err := c.validateDate() if err != nil { - t.Fatalf("Problem creating temp dir at %s: %s\n", tempDir, err) + t.Error(err) } - defer func() { - err = os.RemoveAll(tempDir) - if err != nil { - t.Error(err) - } - }() - var passFile *os.File - passFile, err = ioutil.TempFile(tempDir, "*.start") + c.DataSettings = DataSettings{ + DatabaseData: &DatabaseData{}, + } + err = c.validateDate() + if !errors.Is(err, errStartEndUnset) { + t.Errorf("received: %v, expected: %v", err, errStartEndUnset) + } + c.DataSettings.DatabaseData.StartDate = time.Now() + c.DataSettings.DatabaseData.EndDate = c.DataSettings.DatabaseData.StartDate + err = c.validateDate() + if !errors.Is(err, errBadDate) { + t.Errorf("received: %v, expected: %v", err, errBadDate) + } + c.DataSettings.DatabaseData.EndDate = c.DataSettings.DatabaseData.StartDate.Add(time.Minute) + err = c.validateDate() if err != nil { - t.Fatalf("Problem creating temp file at %v: %s\n", passFile, err) + t.Error(err) } - _, err = passFile.WriteString("{}") + c.DataSettings.APIData = &APIData{} + err = c.validateDate() + if !errors.Is(err, errStartEndUnset) { + t.Errorf("received: %v, expected: %v", err, errStartEndUnset) + } + c.DataSettings.APIData.StartDate = time.Now() + c.DataSettings.APIData.EndDate = c.DataSettings.APIData.StartDate + err = c.validateDate() + if !errors.Is(err, errBadDate) { + t.Errorf("received: %v, expected: %v", err, errBadDate) + } + c.DataSettings.APIData.EndDate = c.DataSettings.APIData.StartDate.Add(time.Minute) + err = c.validateDate() if err != nil { t.Error(err) } - err = passFile.Close() +} + +func TestValidateCurrencySettings(t *testing.T) { + t.Parallel() + c := Config{} + err := c.validateCurrencySettings() + if !errors.Is(err, errNoCurrencySettings) { + t.Errorf("received: %v, expected: %v", err, errNoCurrencySettings) + } + c.CurrencySettings = append(c.CurrencySettings, CurrencySettings{}) + err = c.validateCurrencySettings() + if !errors.Is(err, errUnsetCurrency) { + t.Errorf("received: %v, expected: %v", err, errUnsetCurrency) + } + leet := decimal.NewFromInt(1337) + c.CurrencySettings[0].SpotDetails = &SpotDetails{InitialQuoteFunds: &leet} + err = c.validateCurrencySettings() + if !errors.Is(err, errUnsetCurrency) { + t.Errorf("received: %v, expected: %v", err, errUnsetCurrency) + } + c.CurrencySettings[0].Base = currency.NewCode("lol") + err = c.validateCurrencySettings() + if !errors.Is(err, asset.ErrNotSupported) { + t.Errorf("received: %v, expected: %v", err, asset.ErrNotSupported) + } + c.CurrencySettings[0].Asset = asset.Spot + err = c.validateCurrencySettings() + if !errors.Is(err, errUnsetExchange) { + t.Errorf("received: %v, expected: %v", err, errUnsetExchange) + } + c.CurrencySettings[0].ExchangeName = "lol" + err = c.validateCurrencySettings() if err != nil { t.Error(err) } - _, err = ReadConfigFromFile(passFile.Name()) + + c.CurrencySettings[0].Asset = asset.PerpetualSwap + err = c.validateCurrencySettings() + if !errors.Is(err, errPerpetualsUnsupported) { + t.Errorf("received: %v, expected: %v", err, errPerpetualsUnsupported) + } + + c.CurrencySettings[0].Asset = asset.Futures + c.CurrencySettings[0].Quote = currency.NewCode("PERP") + err = c.validateCurrencySettings() + if !errors.Is(err, errPerpetualsUnsupported) { + t.Errorf("received: %v, expected: %v", err, errPerpetualsUnsupported) + } + + c.CurrencySettings[0].Asset = asset.Spot + c.CurrencySettings[0].MinimumSlippagePercent = decimal.NewFromInt(-1) + err = c.validateCurrencySettings() + if !errors.Is(err, errBadSlippageRates) { + t.Errorf("received: %v, expected: %v", err, errBadSlippageRates) + } + c.CurrencySettings[0].MinimumSlippagePercent = decimal.NewFromInt(2) + c.CurrencySettings[0].MaximumSlippagePercent = decimal.NewFromInt(-1) + err = c.validateCurrencySettings() + if !errors.Is(err, errBadSlippageRates) { + t.Errorf("received: %v, expected: %v", err, errBadSlippageRates) + } + c.CurrencySettings[0].MinimumSlippagePercent = decimal.NewFromInt(2) + c.CurrencySettings[0].MaximumSlippagePercent = decimal.NewFromInt(1) + err = c.validateCurrencySettings() + if !errors.Is(err, errBadSlippageRates) { + t.Errorf("received: %v, expected: %v", err, errBadSlippageRates) + } + + c.CurrencySettings[0].SpotDetails = &SpotDetails{} + err = c.validateCurrencySettings() + if !errors.Is(err, errBadInitialFunds) { + t.Errorf("received: %v, expected: %v", err, errBadInitialFunds) + } + + z := decimal.Zero + c.CurrencySettings[0].SpotDetails.InitialQuoteFunds = &z + c.CurrencySettings[0].SpotDetails.InitialBaseFunds = &z + err = c.validateCurrencySettings() + if !errors.Is(err, errBadInitialFunds) { + t.Errorf("received: %v, expected: %v", err, errBadInitialFunds) + } + + c.CurrencySettings[0].SpotDetails.InitialQuoteFunds = &leet + c.FundingSettings.UseExchangeLevelFunding = true + err = c.validateCurrencySettings() + if !errors.Is(err, errBadInitialFunds) { + t.Errorf("received: %v, expected: %v", err, errBadInitialFunds) + } + + c.CurrencySettings[0].SpotDetails.InitialQuoteFunds = &z + c.CurrencySettings[0].SpotDetails.InitialBaseFunds = &leet + c.FundingSettings.UseExchangeLevelFunding = true + err = c.validateCurrencySettings() + if !errors.Is(err, errBadInitialFunds) { + t.Errorf("received: %v, expected: %v", err, errBadInitialFunds) + } + +} + +func TestValidateMinMaxes(t *testing.T) { + t.Parallel() + c := &Config{} + err := c.validateMinMaxes() if err != nil { t.Error(err) } + + c.CurrencySettings = []CurrencySettings{ + { + SellSide: MinMax{ + MinimumSize: decimal.NewFromInt(-1), + }, + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errSizeLessThanZero) { + t.Errorf("received %v expected %v", err, errSizeLessThanZero) + } + c.CurrencySettings = []CurrencySettings{ + { + SellSide: MinMax{ + MaximumTotal: decimal.NewFromInt(-1), + }, + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errSizeLessThanZero) { + t.Errorf("received %v expected %v", err, errSizeLessThanZero) + } + c.CurrencySettings = []CurrencySettings{ + { + SellSide: MinMax{ + MaximumSize: decimal.NewFromInt(-1), + }, + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errSizeLessThanZero) { + t.Errorf("received %v expected %v", err, errSizeLessThanZero) + } + + c.CurrencySettings = []CurrencySettings{ + { + BuySide: MinMax{ + MinimumSize: decimal.NewFromInt(2), + MaximumTotal: decimal.NewFromInt(10), + MaximumSize: decimal.NewFromInt(1), + }, + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errMaxSizeMinSizeMismatch) { + t.Errorf("received %v expected %v", err, errMaxSizeMinSizeMismatch) + } + + c.CurrencySettings = []CurrencySettings{ + { + BuySide: MinMax{ + MinimumSize: decimal.NewFromInt(2), + MaximumSize: decimal.NewFromInt(2), + }, + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errMinMaxEqual) { + t.Errorf("received %v expected %v", err, errMinMaxEqual) + } + + c.CurrencySettings = []CurrencySettings{ + { + BuySide: MinMax{ + MinimumSize: decimal.NewFromInt(1), + MaximumTotal: decimal.NewFromInt(10), + MaximumSize: decimal.NewFromInt(2), + }, + }, + } + c.PortfolioSettings = PortfolioSettings{ + BuySide: MinMax{ + MinimumSize: decimal.NewFromInt(-1), + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errSizeLessThanZero) { + t.Errorf("received %v expected %v", err, errSizeLessThanZero) + } + c.PortfolioSettings = PortfolioSettings{ + SellSide: MinMax{ + MinimumSize: decimal.NewFromInt(-1), + }, + } + err = c.validateMinMaxes() + if !errors.Is(err, errSizeLessThanZero) { + t.Errorf("received %v expected %v", err, errSizeLessThanZero) + } +} + +func TestValidateStrategySettings(t *testing.T) { + t.Parallel() + c := &Config{} + err := c.validateStrategySettings() + if !errors.Is(err, base.ErrStrategyNotFound) { + t.Errorf("received %v expected %v", err, base.ErrStrategyNotFound) + } + c.StrategySettings = StrategySettings{Name: dca} + err = c.validateStrategySettings() + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } + + c.StrategySettings.SimultaneousSignalProcessing = true + err = c.validateStrategySettings() + if !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } + c.FundingSettings = FundingSettings{} + c.FundingSettings.UseExchangeLevelFunding = true + err = c.validateStrategySettings() + if !errors.Is(err, errExchangeLevelFundingDataRequired) { + t.Errorf("received %v expected %v", err, errExchangeLevelFundingDataRequired) + } + c.FundingSettings.ExchangeLevelFunding = []ExchangeLevelFunding{ + { + InitialFunds: decimal.NewFromInt(-1), + }, + } + err = c.validateStrategySettings() + if !errors.Is(err, errBadInitialFunds) { + t.Errorf("received %v expected %v", err, errBadInitialFunds) + } + + c.StrategySettings.SimultaneousSignalProcessing = false + err = c.validateStrategySettings() + if !errors.Is(err, errSimultaneousProcessingRequired) { + t.Errorf("received %v expected %v", err, errSimultaneousProcessingRequired) + } + + c.FundingSettings.UseExchangeLevelFunding = false + err = c.validateStrategySettings() + if !errors.Is(err, errExchangeLevelFundingRequired) { + t.Errorf("received %v expected %v", err, errExchangeLevelFundingRequired) + } } func TestPrintSettings(t *testing.T) { + t.Parallel() cfg := Config{ Nickname: "super fun run", Goal: "To demonstrate rendering of settings", @@ -106,20 +363,22 @@ func TestPrintSettings(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds1, + InitialBaseFunds: initialQuoteFunds1, }, - BuySide: minMax, - SellSide: minMax, - MakerFee: &makerFee, - TakerFee: &takerFee, + BuySide: minMax, + SellSide: minMax, + MakerFee: &makerFee, + TakerFee: &takerFee, + FuturesDetails: &FuturesDetails{}, }, }, DataSettings: DataSettings{ - Interval: kline.OneMin.Duration(), + Interval: kline.OneMin, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -156,9 +415,74 @@ func TestPrintSettings(t *testing.T) { }, } cfg.PrintSetting() + cfg.FundingSettings = FundingSettings{ + UseExchangeLevelFunding: true, + ExchangeLevelFunding: []ExchangeLevelFunding{{}}, + } + cfg.PrintSetting() +} + +func TestValidate(t *testing.T) { + t.Parallel() + c := &Config{ + StrategySettings: StrategySettings{Name: dca}, + CurrencySettings: []CurrencySettings{ + { + ExchangeName: testExchange, + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, + SpotDetails: &SpotDetails{ + InitialBaseFunds: initialBaseFunds, + InitialQuoteFunds: initialQuoteFunds2, + }, + BuySide: MinMax{ + MinimumSize: decimal.NewFromInt(1), + MaximumSize: decimal.NewFromInt(10), + MaximumTotal: decimal.NewFromInt(10), + }, + }, + }, + } + if err := c.Validate(); !errors.Is(err, nil) { + t.Errorf("received %v expected %v", err, nil) + } +} + +func TestReadConfigFromFile(t *testing.T) { + tempDir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("Problem creating temp dir at %s: %s\n", tempDir, err) + } + defer func() { + err = os.RemoveAll(tempDir) + if err != nil { + t.Error(err) + } + }() + var passFile *os.File + passFile, err = ioutil.TempFile(tempDir, "*.start") + if err != nil { + t.Fatalf("Problem creating temp file at %v: %s\n", passFile, err) + } + _, err = passFile.WriteString("{}") + if err != nil { + t.Error(err) + } + err = passFile.Close() + if err != nil { + t.Error(err) + } + _, err = ReadConfigFromFile(passFile.Name()) + if err != nil { + t.Error(err) + } } func TestGenerateConfigForDCAAPICandles(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCAAPICandles", Goal: "To demonstrate DCA strategy using API candles", @@ -168,9 +492,9 @@ func TestGenerateConfigForDCAAPICandles(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -181,7 +505,7 @@ func TestGenerateConfigForDCAAPICandles(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -217,6 +541,9 @@ func TestGenerateConfigForDCAAPICandles(t *testing.T) { } func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCAAPICandlesExchangeLevelFunding", Goal: "To demonstrate DCA strategy using API candles using a shared pool of funds", @@ -230,8 +557,8 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { ExchangeLevelFunding: []ExchangeLevelFunding{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Currency: currency.USDT.String(), + Asset: asset.Spot, + Currency: currency.USDT, InitialFunds: decimal.NewFromInt(100000), }, }, @@ -239,9 +566,9 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -249,9 +576,9 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.ETH, + Quote: currency.USDT, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -259,7 +586,7 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -295,6 +622,9 @@ func TestGenerateConfigForDCAAPICandlesExchangeLevelFunding(t *testing.T) { } func TestGenerateConfigForDCAAPITrades(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCAAPITrades", Goal: "To demonstrate running the DCA strategy using API trade data", @@ -304,9 +634,9 @@ func TestGenerateConfigForDCAAPITrades(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: "ftx", - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -318,7 +648,7 @@ func TestGenerateConfigForDCAAPITrades(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneHour.Duration(), + Interval: kline.OneHour, DataType: common.TradeStr, APIData: &APIData{ StartDate: startDate, @@ -362,6 +692,9 @@ func TestGenerateConfigForDCAAPITrades(t *testing.T) { } func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCAAPICandlesMultipleCurrencies", Goal: "To demonstrate running the DCA strategy using the API against multiple currencies candle data", @@ -371,9 +704,9 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -384,9 +717,9 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.ETH, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -397,7 +730,7 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -433,6 +766,9 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { } func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCAAPICandlesSimultaneousProcessing", Goal: "To demonstrate how simultaneous processing can work", @@ -443,9 +779,9 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds1, }, @@ -456,9 +792,9 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.ETH, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -469,7 +805,7 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -505,6 +841,9 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { } func TestGenerateConfigForDCALiveCandles(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCALiveCandles", Goal: "To demonstrate live trading proof of concept against candle data", @@ -515,9 +854,9 @@ func TestGenerateConfigForDCALiveCandles(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -528,7 +867,7 @@ func TestGenerateConfigForDCALiveCandles(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneMin.Duration(), + Interval: kline.OneMin, DataType: common.CandleStr, LiveData: &LiveData{ APIKeyOverride: "", @@ -567,6 +906,9 @@ func TestGenerateConfigForDCALiveCandles(t *testing.T) { } func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "TestGenerateRSICandleAPICustomSettingsStrat", Goal: "To demonstrate the RSI strategy using API candle data and custom settings", @@ -581,9 +923,9 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -594,9 +936,9 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.ETH, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialBaseFunds: initialBaseFunds, InitialQuoteFunds: initialQuoteFunds1, @@ -608,7 +950,7 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -644,6 +986,9 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { } func TestGenerateConfigForDCACSVCandles(t *testing.T) { + if !saveConfig { + t.Skip() + } fp := filepath.Join("..", "testdata", "binance_BTCUSDT_24h_2019_01_01_2020_01_01.csv") cfg := Config{ Nickname: "ExampleStrategyDCACSVCandles", @@ -655,9 +1000,9 @@ func TestGenerateConfigForDCACSVCandles(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -668,7 +1013,7 @@ func TestGenerateConfigForDCACSVCandles(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, CSVData: &CSVData{ FullPath: fp, @@ -702,6 +1047,9 @@ func TestGenerateConfigForDCACSVCandles(t *testing.T) { } func TestGenerateConfigForDCACSVTrades(t *testing.T) { + if !saveConfig { + t.Skip() + } fp := filepath.Join("..", "testdata", "binance_BTCUSDT_24h-trades_2020_11_16.csv") cfg := Config{ Nickname: "ExampleStrategyDCACSVTrades", @@ -713,9 +1061,9 @@ func TestGenerateConfigForDCACSVTrades(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -724,7 +1072,7 @@ func TestGenerateConfigForDCACSVTrades(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneMin.Duration(), + Interval: kline.OneMin, DataType: common.TradeStr, CSVData: &CSVData{ FullPath: fp, @@ -756,6 +1104,9 @@ func TestGenerateConfigForDCACSVTrades(t *testing.T) { } func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyDCADatabaseCandles", Goal: "To demonstrate the DCA strategy using database candle data", @@ -765,9 +1116,9 @@ func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, SpotDetails: &SpotDetails{ InitialQuoteFunds: initialQuoteFunds2, }, @@ -778,7 +1129,7 @@ func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, DatabaseData: &DatabaseData{ StartDate: startDate, @@ -823,6 +1174,9 @@ func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { } func TestGenerateConfigForTop2Bottom2(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "ExampleStrategyTop2Bottom2", Goal: "To demonstrate a complex strategy using exchange level funding and simultaneous processing of data signals", @@ -841,14 +1195,14 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { ExchangeLevelFunding: []ExchangeLevelFunding{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Currency: currency.BTC.String(), + Asset: asset.Spot, + Currency: currency.BTC, InitialFunds: decimal.NewFromFloat(3), }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Currency: currency.USDT.String(), + Asset: asset.Spot, + Currency: currency.USDT, InitialFunds: decimal.NewFromInt(10000), }, }, @@ -856,9 +1210,9 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USDT, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -866,9 +1220,9 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.DOGE.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.DOGE, + Quote: currency.USDT, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -876,9 +1230,9 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.ETH.String(), - Quote: currency.BTC.String(), + Asset: asset.Spot, + Base: currency.ETH, + Quote: currency.BTC, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -886,9 +1240,9 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.LTC.String(), - Quote: currency.BTC.String(), + Asset: asset.Spot, + Base: currency.LTC, + Quote: currency.BTC, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -896,9 +1250,9 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.XRP.String(), - Quote: currency.USDT.String(), + Asset: asset.Spot, + Base: currency.XRP, + Quote: currency.USDT, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -906,9 +1260,9 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { }, { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BNB.String(), - Quote: currency.BTC.String(), + Asset: asset.Spot, + Base: currency.BNB, + Quote: currency.BTC, BuySide: minMax, SellSide: minMax, MakerFee: &makerFee, @@ -916,7 +1270,7 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: startDate, @@ -948,265 +1302,10 @@ func TestGenerateConfigForTop2Bottom2(t *testing.T) { } } -func TestValidateDate(t *testing.T) { - c := Config{} - err := c.validateDate() - if err != nil { - t.Error(err) - } - c.DataSettings = DataSettings{ - DatabaseData: &DatabaseData{}, - } - err = c.validateDate() - if !errors.Is(err, errStartEndUnset) { - t.Errorf("received: %v, expected: %v", err, errStartEndUnset) - } - c.DataSettings.DatabaseData.StartDate = time.Now() - c.DataSettings.DatabaseData.EndDate = c.DataSettings.DatabaseData.StartDate - err = c.validateDate() - if !errors.Is(err, errBadDate) { - t.Errorf("received: %v, expected: %v", err, errBadDate) - } - c.DataSettings.DatabaseData.EndDate = c.DataSettings.DatabaseData.StartDate.Add(time.Minute) - err = c.validateDate() - if err != nil { - t.Error(err) - } - c.DataSettings.APIData = &APIData{} - err = c.validateDate() - if !errors.Is(err, errStartEndUnset) { - t.Errorf("received: %v, expected: %v", err, errStartEndUnset) - } - c.DataSettings.APIData.StartDate = time.Now() - c.DataSettings.APIData.EndDate = c.DataSettings.APIData.StartDate - err = c.validateDate() - if !errors.Is(err, errBadDate) { - t.Errorf("received: %v, expected: %v", err, errBadDate) - } - c.DataSettings.APIData.EndDate = c.DataSettings.APIData.StartDate.Add(time.Minute) - err = c.validateDate() - if err != nil { - t.Error(err) - } -} - -func TestValidateCurrencySettings(t *testing.T) { - c := Config{} - err := c.validateCurrencySettings() - if !errors.Is(err, errNoCurrencySettings) { - t.Errorf("received: %v, expected: %v", err, errNoCurrencySettings) - } - c.CurrencySettings = append(c.CurrencySettings, CurrencySettings{}) - err = c.validateCurrencySettings() - if !errors.Is(err, errUnsetCurrency) { - t.Errorf("received: %v, expected: %v", err, errUnsetCurrency) - } - leet := decimal.NewFromInt(1337) - c.CurrencySettings[0].SpotDetails = &SpotDetails{InitialQuoteFunds: &leet} - err = c.validateCurrencySettings() - if !errors.Is(err, errUnsetCurrency) { - t.Errorf("received: %v, expected: %v", err, errUnsetCurrency) - } - c.CurrencySettings[0].Base = "lol" - err = c.validateCurrencySettings() - if !errors.Is(err, errUnsetAsset) { - t.Errorf("received: %v, expected: %v", err, errUnsetAsset) - } - c.CurrencySettings[0].Asset = "lol" - err = c.validateCurrencySettings() - if !errors.Is(err, errUnsetExchange) { - t.Errorf("received: %v, expected: %v", err, errUnsetExchange) - } - c.CurrencySettings[0].ExchangeName = "lol" - err = c.validateCurrencySettings() - if err != nil { - t.Error(err) - } - c.CurrencySettings[0].MinimumSlippagePercent = decimal.NewFromInt(-1) - err = c.validateCurrencySettings() - if !errors.Is(err, errBadSlippageRates) { - t.Errorf("received: %v, expected: %v", err, errBadSlippageRates) - } - c.CurrencySettings[0].MinimumSlippagePercent = decimal.NewFromInt(2) - c.CurrencySettings[0].MaximumSlippagePercent = decimal.NewFromInt(-1) - err = c.validateCurrencySettings() - if !errors.Is(err, errBadSlippageRates) { - t.Errorf("received: %v, expected: %v", err, errBadSlippageRates) - } - c.CurrencySettings[0].MinimumSlippagePercent = decimal.NewFromInt(2) - c.CurrencySettings[0].MaximumSlippagePercent = decimal.NewFromInt(1) - err = c.validateCurrencySettings() - if !errors.Is(err, errBadSlippageRates) { - t.Errorf("received: %v, expected: %v", err, errBadSlippageRates) - } -} - -func TestValidateMinMaxes(t *testing.T) { - t.Parallel() - c := &Config{} - err := c.validateMinMaxes() - if err != nil { - t.Error(err) - } - - c.CurrencySettings = []CurrencySettings{ - { - SellSide: MinMax{ - MinimumSize: decimal.NewFromInt(-1), - }, - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errSizeLessThanZero) { - t.Errorf("received %v expected %v", err, errSizeLessThanZero) - } - c.CurrencySettings = []CurrencySettings{ - { - SellSide: MinMax{ - MaximumTotal: decimal.NewFromInt(-1), - }, - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errSizeLessThanZero) { - t.Errorf("received %v expected %v", err, errSizeLessThanZero) - } - c.CurrencySettings = []CurrencySettings{ - { - SellSide: MinMax{ - MaximumSize: decimal.NewFromInt(-1), - }, - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errSizeLessThanZero) { - t.Errorf("received %v expected %v", err, errSizeLessThanZero) - } - - c.CurrencySettings = []CurrencySettings{ - { - BuySide: MinMax{ - MinimumSize: decimal.NewFromInt(2), - MaximumTotal: decimal.NewFromInt(10), - MaximumSize: decimal.NewFromInt(1), - }, - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errMaxSizeMinSizeMismatch) { - t.Errorf("received %v expected %v", err, errMaxSizeMinSizeMismatch) - } - - c.CurrencySettings = []CurrencySettings{ - { - BuySide: MinMax{ - MinimumSize: decimal.NewFromInt(2), - MaximumSize: decimal.NewFromInt(2), - }, - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errMinMaxEqual) { - t.Errorf("received %v expected %v", err, errMinMaxEqual) - } - - c.CurrencySettings = []CurrencySettings{ - { - BuySide: MinMax{ - MinimumSize: decimal.NewFromInt(1), - MaximumTotal: decimal.NewFromInt(10), - MaximumSize: decimal.NewFromInt(2), - }, - }, - } - c.PortfolioSettings = PortfolioSettings{ - BuySide: MinMax{ - MinimumSize: decimal.NewFromInt(-1), - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errSizeLessThanZero) { - t.Errorf("received %v expected %v", err, errSizeLessThanZero) - } - c.PortfolioSettings = PortfolioSettings{ - SellSide: MinMax{ - MinimumSize: decimal.NewFromInt(-1), - }, - } - err = c.validateMinMaxes() - if !errors.Is(err, errSizeLessThanZero) { - t.Errorf("received %v expected %v", err, errSizeLessThanZero) - } -} - -func TestValidateStrategySettings(t *testing.T) { - t.Parallel() - c := &Config{} - err := c.validateStrategySettings() - if !errors.Is(err, base.ErrStrategyNotFound) { - t.Errorf("received %v expected %v", err, base.ErrStrategyNotFound) - } - c.StrategySettings = StrategySettings{Name: dca} - err = c.validateStrategySettings() - if !errors.Is(err, nil) { - t.Errorf("received %v expected %v", err, nil) - } - - c.StrategySettings.SimultaneousSignalProcessing = true - err = c.validateStrategySettings() - if !errors.Is(err, nil) { - t.Errorf("received %v expected %v", err, nil) - } - c.FundingSettings = FundingSettings{} - c.FundingSettings.UseExchangeLevelFunding = true - err = c.validateStrategySettings() - if !errors.Is(err, errExchangeLevelFundingDataRequired) { - t.Errorf("received %v expected %v", err, errExchangeLevelFundingDataRequired) - } - c.FundingSettings.ExchangeLevelFunding = []ExchangeLevelFunding{ - { - InitialFunds: decimal.NewFromInt(-1), - }, - } - err = c.validateStrategySettings() - if !errors.Is(err, errBadInitialFunds) { - t.Errorf("received %v expected %v", err, errBadInitialFunds) - } - c.FundingSettings.UseExchangeLevelFunding = false - err = c.validateStrategySettings() - if !errors.Is(err, errExchangeLevelFundingRequired) { - t.Errorf("received %v expected %v", err, errExchangeLevelFundingRequired) - } -} - -func TestValidate(t *testing.T) { - t.Parallel() - c := &Config{ - StrategySettings: StrategySettings{Name: dca}, - CurrencySettings: []CurrencySettings{ - { - ExchangeName: testExchange, - Asset: asset.Spot.String(), - Base: currency.BTC.String(), - Quote: currency.USDT.String(), - SpotDetails: &SpotDetails{ - InitialBaseFunds: initialBaseFunds, - InitialQuoteFunds: initialQuoteFunds2, - }, - BuySide: MinMax{ - MinimumSize: decimal.NewFromInt(1), - MaximumSize: decimal.NewFromInt(10), - MaximumTotal: decimal.NewFromInt(10), - }, - }, - }, - } - if err := c.Validate(); !errors.Is(err, nil) { - t.Errorf("received %v expected %v", err, nil) - } -} - func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { + if !saveConfig { + t.Skip() + } cfg := Config{ Nickname: "Example Cash and Carry", Goal: "To demonstrate a cash and carry strategy", @@ -1219,8 +1318,8 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { ExchangeLevelFunding: []ExchangeLevelFunding{ { ExchangeName: "ftx", - Asset: "spot", - Currency: "USD", + Asset: asset.Spot, + Currency: currency.USD, InitialFunds: *initialQuoteFunds2, }, }, @@ -1228,23 +1327,23 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { CurrencySettings: []CurrencySettings{ { ExchangeName: "ftx", - Asset: asset.Futures.String(), - Base: "BTC", - Quote: "20210924", + Asset: asset.Futures, + Base: currency.BTC, + Quote: currency.NewCode("20210924"), MakerFee: &makerFee, TakerFee: &takerFee, }, { ExchangeName: "ftx", - Asset: asset.Spot.String(), - Base: "BTC", - Quote: "USD", + Asset: asset.Spot, + Base: currency.BTC, + Quote: currency.USD, MakerFee: &makerFee, TakerFee: &takerFee, }, }, DataSettings: DataSettings{ - Interval: kline.OneDay.Duration(), + Interval: kline.OneDay, DataType: common.CandleStr, APIData: &APIData{ StartDate: time.Date(2021, 1, 14, 0, 0, 0, 0, time.UTC), diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 19114fd0cd9..1aa4b5c0a27 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -5,7 +5,10 @@ import ( "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/database" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) // Errors for config validation @@ -42,12 +45,12 @@ type Config struct { // DataSettings is a container for each type of data retrieval setting. // Only ONE can be populated per config type DataSettings struct { - Interval time.Duration `json:"interval"` - DataType string `json:"data-type"` - APIData *APIData `json:"api-data,omitempty"` - DatabaseData *DatabaseData `json:"database-data,omitempty"` - LiveData *LiveData `json:"live-data,omitempty"` - CSVData *CSVData `json:"csv-data,omitempty"` + Interval kline.Interval `json:"interval"` + DataType string `json:"data-type"` + APIData *APIData `json:"api-data,omitempty"` + DatabaseData *DatabaseData `json:"database-data,omitempty"` + LiveData *LiveData `json:"live-data,omitempty"` + CSVData *CSVData `json:"csv-data,omitempty"` } // FundingSettings contains funding details for individual currencies @@ -78,8 +81,8 @@ type StrategySettings struct { // will have dibs type ExchangeLevelFunding struct { ExchangeName string `json:"exchange-name"` - Asset string `json:"asset"` - Currency string `json:"currency"` + Asset asset.Item `json:"asset"` + Currency currency.Code `json:"currency"` InitialFunds decimal.Decimal `json:"initial-funds"` TransferFee decimal.Decimal `json:"transfer-fee"` } @@ -104,10 +107,10 @@ type PortfolioSettings struct { type Leverage struct { CanUseLeverage bool `json:"can-use-leverage"` MaximumOrdersWithLeverageRatio decimal.Decimal `json:"maximum-orders-with-leverage-ratio"` - // this means you can place an order with higher leverage rate. eg have $100 in collateral, + // MaximumOrderLeverageRate allows for orders to be placed with higher leverage rate. eg have $100 in collateral, // but place an order for $200 using 2x leverage MaximumOrderLeverageRate decimal.Decimal `json:"maximum-leverage-rate"` - // this means you can place orders at `1x leverage, but utilise collateral as leverage to place more. + // MaximumCollateralLeverageRate allows for orders to be placed at `1x leverage, but utilise collateral as leverage to place more. // eg if this is 2x, and collateral is $100 I can place two long/shorts of $100 MaximumCollateralLeverageRate decimal.Decimal `json:"maximum-collateral-leverage-rate"` } @@ -124,10 +127,10 @@ type MinMax struct { // you wish to trade with // Backtester will load the data of the currencies specified here type CurrencySettings struct { - ExchangeName string `json:"exchange-name"` - Asset string `json:"asset"` - Base string `json:"base"` - Quote string `json:"quote"` + ExchangeName string `json:"exchange-name"` + Asset asset.Item `json:"asset"` + Base currency.Code `json:"base"` + Quote currency.Code `json:"quote"` // USDTrackingPair is used for price tracking data only USDTrackingPair bool `json:"-"` @@ -152,7 +155,7 @@ type CurrencySettings struct { } // SpotDetails contains funding information that cannot be shared with another -// pair during the backtesting run. Use exchange level funding to shared funds +// pair during the backtesting run. Use exchange level funding to share funds type SpotDetails struct { InitialBaseFunds *decimal.Decimal `json:"initial-base-funds,omitempty"` InitialQuoteFunds *decimal.Decimal `json:"initial-quote-funds,omitempty"` diff --git a/backtester/data/data.go b/backtester/data/data.go index a70ff209542..43ef41ffd10 100644 --- a/backtester/data/data.go +++ b/backtester/data/data.go @@ -39,6 +39,9 @@ func (h *HandlerPerCurrency) GetAllData() map[string]map[asset.Item]map[currency // GetDataForCurrency returns the Handler for a specific exchange, asset, currency func (h *HandlerPerCurrency) GetDataForCurrency(ev common.EventHandler) (Handler, error) { + if ev == nil { + return nil, common.ErrNilEvent + } handler, ok := h.data[ev.GetExchange()][ev.GetAssetType()][ev.Pair()] if !ok { return nil, fmt.Errorf("%s %s %s %w", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), ErrHandlerNotFound) diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index 3793e322cf9..ff22008665c 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -1,10 +1,12 @@ package data import ( + "errors" "testing" "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/currency" @@ -18,12 +20,19 @@ type fakeDataHandler struct { time int } -func TestBaseDataFunctions(t *testing.T) { +func TestLatest(t *testing.T) { t.Parallel() var d Base - if latest := d.Latest(); latest != nil { - t.Error("expected nil") + d.AppendStream(&fakeDataHandler{time: 1}) + latest := d.Latest() + if latest != d.stream[d.offset] { + t.Error("expected latest to match offset") } +} + +func TestBaseDataFunctions(t *testing.T) { + t.Parallel() + var d Base d.Next() o := d.Offset() @@ -43,6 +52,7 @@ func TestBaseDataFunctions(t *testing.T) { d.AppendStream(&fakeDataHandler{time: 3}) d.AppendStream(&fakeDataHandler{time: 4}) d.Next() + d.Next() if list := d.List(); len(list) != 2 { t.Errorf("expected 2 received %v", len(list)) @@ -62,6 +72,7 @@ func TestBaseDataFunctions(t *testing.T) { if history := d.History(); len(history) != 4 { t.Errorf("expected 4 received %v", len(history)) } + d.SetStream(nil) if st := d.GetStream(); st != nil { t.Error("expected nil") @@ -69,6 +80,7 @@ func TestBaseDataFunctions(t *testing.T) { d.Reset() d.GetStream() d.SortStream() + } func TestSetup(t *testing.T) { @@ -80,56 +92,6 @@ func TestSetup(t *testing.T) { } } -/* -func TestStream(t *testing.T) { - var d Base - var f fakeDataHandler - - // shut up coverage report - f.GetOffset() - f.SetOffset(1) - f.IsOrder() - f.Pair() - f.GetExchange() - f.GetInterval() - f.GetAssetType() - f.GetReason() - f.AppendReason("fake") - f.GetClosePrice() - f.GetHighPrice() - f.GetLowPrice() - f.GetOpenPrice() - - d.AppendStream(fakeDataHandler{time: 1}) - d.AppendStream(fakeDataHandler{time: 4}) - d.AppendStream(fakeDataHandler{time: 10}) - d.AppendStream(fakeDataHandler{time: 2}) - d.AppendStream(fakeDataHandler{time: 20}) - - d.SortStream() - - f, ok := d.Next().(fakeDataHandler) - if f.time != 1 || !ok { - t.Error("expected 1") - } - f, ok = d.Next().(fakeDataHandler) - if f.time != 2 || !ok { - t.Error("expected 2") - } - f, ok = d.Next().(fakeDataHandler) - if f.time != 4 || !ok { - t.Error("expected 4") - } - f, ok = d.Next().(fakeDataHandler) - if f.time != 10 || !ok { - t.Error("expected 10") - } - f, ok = d.Next().(fakeDataHandler) - if f.time != 20 || !ok { - t.Error("expected 20") - } -} -*/ func TestSetDataForCurrency(t *testing.T) { t.Parallel() d := HandlerPerCurrency{} @@ -165,13 +127,12 @@ func TestGetAllData(t *testing.T) { func TestGetDataForCurrency(t *testing.T) { t.Parallel() d := HandlerPerCurrency{} - exch := testExchange a := asset.Spot p := currency.NewPair(currency.BTC, currency.USDT) - d.SetDataForCurrency(exch, a, p, nil) - d.SetDataForCurrency(exch, a, currency.NewPair(currency.BTC, currency.DOGE), nil) + d.SetDataForCurrency(testExchange, a, p, nil) + d.SetDataForCurrency(testExchange, a, currency.NewPair(currency.BTC, currency.DOGE), nil) ev := &order.Order{Base: event.Base{ - Exchange: exch, + Exchange: testExchange, AssetType: a, CurrencyPair: p, }} @@ -182,6 +143,29 @@ func TestGetDataForCurrency(t *testing.T) { if result != nil { t.Error("expected nil") } + + _, err = d.GetDataForCurrency(nil) + if !errors.Is(err, common.ErrNilEvent) { + t.Errorf("received '%v' expected '%v'", err, common.ErrNilEvent) + } + + _, err = d.GetDataForCurrency(&order.Order{Base: event.Base{ + Exchange: "lol", + AssetType: asset.USDTMarginedFutures, + CurrencyPair: currency.NewPair(currency.EMB, currency.DOGE), + }}) + if !errors.Is(err, ErrHandlerNotFound) { + t.Errorf("received '%v' expected '%v'", err, ErrHandlerNotFound) + } + + _, err = d.GetDataForCurrency(&order.Order{Base: event.Base{ + Exchange: testExchange, + AssetType: asset.USDTMarginedFutures, + CurrencyPair: currency.NewPair(currency.EMB, currency.DOGE), + }}) + if !errors.Is(err, ErrHandlerNotFound) { + t.Errorf("received '%v' expected '%v'", err, ErrHandlerNotFound) + } } func TestReset(t *testing.T) { diff --git a/backtester/data/kline/csv/csv.go b/backtester/data/kline/csv/csv.go index 446570bbaa8..38043a54070 100644 --- a/backtester/data/kline/csv/csv.go +++ b/backtester/data/kline/csv/csv.go @@ -33,7 +33,7 @@ func LoadData(dataType int64, filepath, exchangeName string, interval time.Durat defer func() { err = csvFile.Close() if err != nil { - log.Errorln(common.SubLoggers[common.Data], err) + log.Errorln(common.Data, err) } }() diff --git a/backtester/data/kline/database/database.go b/backtester/data/kline/database/database.go index af6e50253ab..e8ae596120f 100644 --- a/backtester/data/kline/database/database.go +++ b/backtester/data/kline/database/database.go @@ -38,7 +38,7 @@ func LoadData(startDate, endDate time.Time, interval time.Duration, exchangeName resp.Item = klineItem for i := range klineItem.Candles { if klineItem.Candles[i].ValidationIssues != "" { - log.Warnf(common.SubLoggers[common.Data], "candle validation issue for %v %v %v: %v", klineItem.Exchange, klineItem.Asset, klineItem.Pair, klineItem.Candles[i].ValidationIssues) + log.Warnf(common.Data, "candle validation issue for %v %v %v: %v", klineItem.Exchange, klineItem.Asset, klineItem.Pair, klineItem.Candles[i].ValidationIssues) } } case common.DataTrade: diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index be8c4d85363..3225f859bec 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -29,7 +29,7 @@ func (d *DataFromKline) Load() error { klineData := make([]common.DataEventHandler, len(d.Item.Candles)) for i := range d.Item.Candles { - klinerino := &kline.Kline{ + newKline := &kline.Kline{ Base: event.Base{ Offset: int64(i + 1), Exchange: d.Item.Exchange, @@ -46,7 +46,7 @@ func (d *DataFromKline) Load() error { Volume: decimal.NewFromFloat(d.Item.Candles[i].Volume), ValidationIssues: d.Item.Candles[i].ValidationIssues, } - klineData[i] = klinerino + klineData[i] = newKline d.addedTimes[d.Item.Candles[i].Time.UTC().Unix()] = true } @@ -95,7 +95,7 @@ func (d *DataFromKline) AppendResults(ki *gctkline.Item) { d.RangeHolder.Ranges[i].Intervals[j].HasData = true } } - log.Debugf(common.SubLoggers[common.Data], "appending %v candle intervals: %v", len(gctCandles), candleTimes) + log.Debugf(common.Data, "appending %v candle intervals: %v", len(gctCandles), candleTimes) d.AppendStream(klineData...) d.SortStream() } @@ -110,7 +110,7 @@ func (d *DataFromKline) StreamOpen() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Open } else { - log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") + log.Errorf(common.Data, "incorrect data loaded into stream") } } return ret @@ -126,7 +126,7 @@ func (d *DataFromKline) StreamHigh() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.High } else { - log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") + log.Errorf(common.Data, "incorrect data loaded into stream") } } return ret @@ -142,7 +142,7 @@ func (d *DataFromKline) StreamLow() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Low } else { - log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") + log.Errorf(common.Data, "incorrect data loaded into stream") } } return ret @@ -158,7 +158,7 @@ func (d *DataFromKline) StreamClose() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Close } else { - log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") + log.Errorf(common.Data, "incorrect data loaded into stream") } } return ret @@ -174,7 +174,7 @@ func (d *DataFromKline) StreamVol() []decimal.Decimal { if val, ok := s[x].(*kline.Kline); ok { ret[x] = val.Volume } else { - log.Errorf(common.SubLoggers[common.Data], "incorrect data loaded into stream") + log.Errorf(common.Data, "incorrect data loaded into stream") } } return ret diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 70215173f29..352f8150ad1 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -48,8 +48,8 @@ func (bt *BackTest) Reset() { // Run will iterate over loaded data events // save them and then handle the event based on its type -func (bt *BackTest) Run() error { - log.Info(common.SubLoggers[common.Backtester], "running backtester against pre-defined data") +func (bt *BackTest) Run() { + log.Info(common.Backtester, "running backtester against pre-defined data") dataLoadingIssue: for ev := bt.EventQueue.NextEvent(); ; ev = bt.EventQueue.NextEvent() { if ev == nil { @@ -61,7 +61,7 @@ dataLoadingIssue: d := dataHandler.Next() if d == nil { if !bt.hasHandledEvent { - log.Errorf(common.SubLoggers[common.Backtester], "Unable to perform `Next` for %v %v %v", exchangeName, assetItem, currencyPair) + log.Errorf(common.Backtester, "Unable to perform `Next` for %v %v %v", exchangeName, assetItem, currencyPair) } break dataLoadingIssue } @@ -79,15 +79,13 @@ dataLoadingIssue: } else { err := bt.handleEvent(ev) if err != nil { - log.Error(common.SubLoggers[common.Backtester], err) + log.Error(common.Backtester, err) } } if !bt.hasHandledEvent { bt.hasHandledEvent = true } } - - return nil } // handleEvent is the main processor of data for the backtester @@ -136,6 +134,7 @@ func (bt *BackTest) handleEvent(ev common.EventHandler) error { return nil } +// processSingleDataEvent will pass the event to the strategy and determine how it should be handled func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds funding.IFundReleaser) error { err := bt.updateStatsForDataEvent(ev, funds) if err != nil { @@ -151,12 +150,12 @@ func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds fun // too much bad data is a severe error and backtesting must cease return err } - log.Errorf(common.SubLoggers[common.Backtester], "OnSignal %v", err) + log.Errorf(common.Backtester, "OnSignal %v", err) return nil } err = bt.Statistic.SetEventForOffset(s) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v", err) + log.Errorf(common.Backtester, "SetEventForOffset %v", err) } bt.EventQueue.AppendEvent(s) @@ -164,13 +163,8 @@ func (bt *BackTest) processSingleDataEvent(ev common.DataEventHandler, funds fun } // processSimultaneousDataEvents determines what signal events are generated and appended -// to the event queue based on whether it is running a multi-currency consideration strategy order not -// -// for multi-currency-consideration it will pass all currency datas to the strategy for it to determine what +// to the event queue. It will pass all currency events to the strategy to determine what // currencies to act upon -// -// for non-multi-currency-consideration strategies, it will simply process every currency individually -// against the strategy and generate signals func (bt *BackTest) processSimultaneousDataEvents() error { var dataEvents []data.Handler dataHandlerMap := bt.Datas.GetAllData() @@ -190,7 +184,7 @@ func (bt *BackTest) processSimultaneousDataEvents() error { case errors.Is(err, gctorder.ErrPositionLiquidated): return nil default: - log.Error(common.SubLoggers[common.Backtester], err) + log.Error(common.Backtester, err) } } dataEvents = append(dataEvents, dataHandler) @@ -203,13 +197,13 @@ func (bt *BackTest) processSimultaneousDataEvents() error { // too much bad data is a severe error and backtesting must cease return err } - log.Errorf(common.SubLoggers[common.Backtester], "OnSimultaneousSignals %v", err) + log.Errorf(common.Backtester, "OnSimultaneousSignals %v", err) return nil } for i := range signals { err = bt.Statistic.SetEventForOffset(signals[i]) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", signals[i].GetExchange(), signals[i].GetAssetType(), signals[i].Pair(), err) + log.Errorf(common.Backtester, "SetEventForOffset %v %v %v %v", signals[i].GetExchange(), signals[i].GetAssetType(), signals[i].Pair(), err) } bt.EventQueue.AppendEvent(signals[i]) } @@ -231,12 +225,12 @@ func (bt *BackTest) updateStatsForDataEvent(ev common.DataEventHandler, funds fu if errors.Is(err, statistics.ErrAlreadyProcessed) { return err } - log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v", err) + log.Errorf(common.Backtester, "SetupEventForTime %v", err) } // update portfolio manager with the latest price err = bt.Portfolio.UpdateHoldings(ev, funds) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "UpdateHoldings %v", err) + log.Errorf(common.Backtester, "UpdateHoldings %v", err) } if ev.GetAssetType().IsFutures() { @@ -311,17 +305,13 @@ func (bt *BackTest) triggerLiquidationsForExchange(ev common.DataEventHandler, p bt.EventQueue.AppendEvent(orders[i]) err = bt.Statistic.SetEventForOffset(orders[i]) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "SetupEventForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.Funding.Liquidate(orders[i]) } pnl.Result.IsLiquidated = true pnl.Result.Status = gctorder.Liquidated - pnlErr := bt.Statistic.AddPNLForTime(pnl) - if pnlErr != nil { - return pnlErr - } - return nil + return bt.Statistic.AddPNLForTime(pnl) } // processSignalEvent receives an event from the strategy for processing under the portfolio @@ -334,13 +324,13 @@ func (bt *BackTest) processSignalEvent(ev signal.Event, funds funding.IFundReser } cs, err := bt.Exchange.GetCurrencySettings(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return fmt.Errorf("GetCurrencySettings %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var o *order.Order o, err = bt.Portfolio.OnSignal(ev, &cs, funds) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) return fmt.Errorf("OnSignal %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } err = bt.Statistic.SetEventForOffset(o) @@ -366,16 +356,16 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease f, err := bt.Exchange.ExecuteOrder(ev, d, bt.orderManager, funds) if err != nil { if f == nil { - log.Errorf(common.SubLoggers[common.Backtester], "ExecuteOrder fill event should always be returned, please fix, %v", err) + log.Errorf(common.Backtester, "ExecuteOrder fill event should always be returned, please fix, %v", err) return fmt.Errorf("ExecuteOrder fill event should always be returned, please fix, %v", err) } if !errors.Is(err, exchange.ErrDoNothing) { - log.Errorf(common.SubLoggers[common.Backtester], "ExecuteOrder %v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) + log.Errorf(common.Backtester, "ExecuteOrder %v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) } } err = bt.Statistic.SetEventForOffset(f) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } bt.EventQueue.AppendEvent(f) return nil @@ -388,33 +378,33 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) } err = bt.Statistic.SetEventForOffset(t) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "SetEventForOffset %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } var holding *holdings.Holding holding, err = bt.Portfolio.ViewHoldingAtTimePeriod(ev) if err != nil { - log.Error(common.SubLoggers[common.Backtester], err) + log.Error(common.Backtester, err) } if holding == nil { - log.Error(common.SubLoggers[common.Backtester], "ViewHoldingAtTimePeriod why is holdings nil?") + log.Error(common.Backtester, "ViewHoldingAtTimePeriod why is holdings nil?") } else { err = bt.Statistic.AddHoldingsForTime(holding) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } var cp *compliance.Manager cp, err = bt.Portfolio.GetComplianceManager(ev.GetExchange(), ev.GetAssetType(), ev.Pair()) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "GetComplianceManager %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "GetComplianceManager %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } snap := cp.GetLatestSnapshot() err = bt.Statistic.AddComplianceSnapshotForTime(snap, ev) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "AddComplianceSnapshotForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "AddComplianceSnapshotForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } fde := ev.GetFillDependentEvent() @@ -423,16 +413,13 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) fde.SetOffset(ev.GetOffset()) err = bt.Statistic.SetEventForOffset(fde) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "SetEventForOffset %v %v %v %v", fde.GetExchange(), fde.GetAssetType(), fde.Pair(), err) + log.Errorf(common.Backtester, "SetEventForOffset %v %v %v %v", fde.GetExchange(), fde.GetAssetType(), fde.Pair(), err) } fde.AppendReasonf("raising event after %v %v %v fill", ev.GetExchange(), ev.GetAssetType(), ev.Pair()) bt.EventQueue.AppendEvent(fde) } if ev.GetAssetType().IsFutures() { - err = bt.processFuturesFillEvent(ev, funds) - if err != nil { - return err - } + return bt.processFuturesFillEvent(ev, funds) } return nil } @@ -466,7 +453,7 @@ func (bt *BackTest) processFuturesFillEvent(ev fill.Event, funds funding.IFundRe err = bt.Statistic.AddPNLForTime(pnl) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) + log.Errorf(common.Backtester, "AddHoldingsForTime %v %v %v %v", ev.GetExchange(), ev.GetAssetType(), ev.Pair(), err) } } err := bt.Funding.UpdateCollateral(ev) diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 6acdb237fd0..58eeba90dd6 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -87,14 +87,14 @@ func TestNewFromConfig(t *testing.T) { cfg.CurrencySettings = []config.CurrencySettings{ { ExchangeName: "test", - Base: "test", - Quote: "test", + Base: currency.NewCode("test"), + Quote: currency.NewCode("test"), }, { ExchangeName: testExchange, - Base: "BTC", - Quote: "0624", - Asset: asset.Futures.String(), + Base: currency.BTC, + Quote: currency.NewCode("0624"), + Asset: asset.Futures, }, } _, err = NewFromConfig(cfg, "", "", false) @@ -103,10 +103,10 @@ func TestNewFromConfig(t *testing.T) { } cfg.CurrencySettings[0].ExchangeName = testExchange _, err = NewFromConfig(cfg, "", "", false) - if !errors.Is(err, errInvalidConfigAsset) { - t.Errorf("received: %v, expected: %v", err, errInvalidConfigAsset) + if !errors.Is(err, asset.ErrNotSupported) { + t.Errorf("received: %v, expected: %v", err, asset.ErrNotSupported) } - cfg.CurrencySettings[0].Asset = asset.Spot.String() + cfg.CurrencySettings[0].Asset = asset.Spot _, err = NewFromConfig(cfg, "", "", false) if !errors.Is(err, base.ErrStrategyNotFound) { t.Errorf("received: %v, expected: %v", err, base.ErrStrategyNotFound) @@ -118,8 +118,8 @@ func TestNewFromConfig(t *testing.T) { "hello": "moto", }, } - cfg.CurrencySettings[0].Base = "BTC" - cfg.CurrencySettings[0].Quote = "USD" + cfg.CurrencySettings[0].Base = currency.BTC + cfg.CurrencySettings[0].Quote = currency.USD cfg.DataSettings.APIData = &config.APIData{ StartDate: time.Time{}, EndDate: time.Time{}, @@ -134,7 +134,7 @@ func TestNewFromConfig(t *testing.T) { if !errors.Is(err, errIntervalUnset) { t.Errorf("received: %v, expected: %v", err, errIntervalUnset) } - cfg.DataSettings.Interval = gctkline.OneMin.Duration() + cfg.DataSettings.Interval = gctkline.OneMin cfg.CurrencySettings[0].MakerFee = &decimal.Zero cfg.CurrencySettings[0].TakerFee = &decimal.Zero _, err = NewFromConfig(cfg, "", "", false) @@ -154,15 +154,15 @@ func TestNewFromConfig(t *testing.T) { cfg.FundingSettings.ExchangeLevelFunding = []config.ExchangeLevelFunding{ { ExchangeName: testExchange, - Asset: asset.Spot.String(), - Currency: currency.BTC.String(), + Asset: asset.Spot, + Currency: currency.BTC, InitialFunds: leet, TransferFee: leet, }, { ExchangeName: testExchange, - Asset: asset.Futures.String(), - Currency: currency.BTC.String(), + Asset: asset.Futures, + Currency: currency.BTC, InitialFunds: leet, TransferFee: leet, }, @@ -183,9 +183,9 @@ func TestLoadDataAPI(t *testing.T) { CurrencySettings: []config.CurrencySettings{ { ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), + Asset: asset.Spot, + Base: cp.Base, + Quote: cp.Quote, SpotDetails: &config.SpotDetails{ InitialQuoteFunds: &leet, }, @@ -197,7 +197,7 @@ func TestLoadDataAPI(t *testing.T) { }, DataSettings: config.DataSettings{ DataType: common.CandleStr, - Interval: gctkline.OneMin.Duration(), + Interval: gctkline.OneMin, APIData: &config.APIData{ StartDate: time.Now().Add(-time.Minute), EndDate: time.Now(), @@ -240,9 +240,9 @@ func TestLoadDataDatabase(t *testing.T) { CurrencySettings: []config.CurrencySettings{ { ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), + Asset: asset.Spot, + Base: cp.Base, + Quote: cp.Quote, SpotDetails: &config.SpotDetails{ InitialQuoteFunds: &leet, }, @@ -254,7 +254,7 @@ func TestLoadDataDatabase(t *testing.T) { }, DataSettings: config.DataSettings{ DataType: common.CandleStr, - Interval: gctkline.OneMin.Duration(), + Interval: gctkline.OneMin, DatabaseData: &config.DatabaseData{ Config: database.Config{ Enabled: true, @@ -308,9 +308,9 @@ func TestLoadDataCSV(t *testing.T) { CurrencySettings: []config.CurrencySettings{ { ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), + Asset: asset.Spot, + Base: cp.Base, + Quote: cp.Quote, SpotDetails: &config.SpotDetails{ InitialQuoteFunds: &leet, }, @@ -322,7 +322,7 @@ func TestLoadDataCSV(t *testing.T) { }, DataSettings: config.DataSettings{ DataType: common.CandleStr, - Interval: gctkline.OneMin.Duration(), + Interval: gctkline.OneMin, CSVData: &config.CSVData{ FullPath: "test", }}, @@ -366,9 +366,9 @@ func TestLoadDataLive(t *testing.T) { CurrencySettings: []config.CurrencySettings{ { ExchangeName: "Binance", - Asset: asset.Spot.String(), - Base: cp.Base.String(), - Quote: cp.Quote.String(), + Asset: asset.Spot, + Base: cp.Base, + Quote: cp.Quote, SpotDetails: &config.SpotDetails{ InitialQuoteFunds: &leet, }, @@ -380,7 +380,7 @@ func TestLoadDataLive(t *testing.T) { }, DataSettings: config.DataSettings{ DataType: common.CandleStr, - Interval: gctkline.OneMin.Duration(), + Interval: gctkline.OneMin, LiveData: &config.LiveData{ APIKeyOverride: "test", APISecretOverride: "test", @@ -457,7 +457,7 @@ func TestLoadLiveData(t *testing.T) { RealOrders: true, } - cfg.DataSettings.Interval = gctkline.OneDay.Duration() + cfg.DataSettings.Interval = gctkline.OneDay cfg.DataSettings.DataType = common.CandleStr err = loadLiveData(cfg, b) if err != nil { @@ -596,10 +596,7 @@ func TestFullCycle(t *testing.T) { } bt.Datas.SetDataForCurrency(ex, a, cp, &k) - err = bt.Run() - if err != nil { - t.Error(err) - } + bt.Run() } func TestStop(t *testing.T) { @@ -709,10 +706,7 @@ func TestFullCycleMulti(t *testing.T) { bt.Datas.SetDataForCurrency(ex, a, cp, &k) - err = bt.Run() - if err != nil { - t.Error(err) - } + bt.Run() } func TestTriggerLiquidationsForExchange(t *testing.T) { @@ -749,11 +743,11 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { CurrencyPair: cp, AssetType: a, }, - Open: decimal.NewFromInt(1337), - Close: decimal.NewFromInt(1337), - Low: decimal.NewFromInt(1337), - High: decimal.NewFromInt(1337), - Volume: decimal.NewFromInt(1337), + Open: leet, + Close: leet, + Low: leet, + High: leet, + Volume: leet, }}) d.Next() da := &kline.DataFromKline{ diff --git a/backtester/engine/live.go b/backtester/engine/live.go index c4e299a1a04..9cc3232884d 100644 --- a/backtester/engine/live.go +++ b/backtester/engine/live.go @@ -20,7 +20,7 @@ import ( // It runs by constantly checking for new live datas and running through the list of events // once new data is processed. It will run until application close event has been received func (bt *BackTest) RunLive() error { - log.Info(common.SubLoggers[common.Backtester], "running backtester against live data") + log.Info(common.Backtester, "running backtester against live data") timeoutTimer := time.NewTimer(time.Minute * 5) // a frequent timer so that when a new candle is released by an exchange // that it can be processed quickly @@ -69,24 +69,24 @@ func (bt *BackTest) RunLive() error { // loadLiveDataLoop is an incomplete function to continuously retrieve exchange data on a loop // from live. Its purpose is to be able to perform strategy analysis against current data func (bt *BackTest) loadLiveDataLoop(resp *kline.DataFromKline, cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, dataType int64) { - startDate := time.Now().Add(-cfg.DataSettings.Interval * 2) + startDate := time.Now().Add(-cfg.DataSettings.Interval.Duration() * 2) dates, err := gctkline.CalculateCandleDateRanges( startDate, startDate.AddDate(1, 0, 0), gctkline.Interval(cfg.DataSettings.Interval), 0) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "%v. Please check your GoCryptoTrader configuration", err) + log.Errorf(common.Backtester, "%v. Please check your GoCryptoTrader configuration", err) return } candles, err := live.LoadData(context.TODO(), exch, dataType, - cfg.DataSettings.Interval, + cfg.DataSettings.Interval.Duration(), fPair, a) if err != nil { - log.Errorf(common.SubLoggers[common.Backtester], "%v. Please check your GoCryptoTrader configuration", err) + log.Errorf(common.Backtester, "%v. Please check your GoCryptoTrader configuration", err) return } dates.SetHasDataFromCandles(candles.Candles) @@ -99,11 +99,11 @@ func (bt *BackTest) loadLiveDataLoop(resp *kline.DataFromKline, cfg *config.Conf case <-bt.shutdown: return case <-loadNewDataTimer.C: - log.Infof(common.SubLoggers[common.Backtester], "fetching data for %v %v %v %v", exch.GetName(), a, fPair, cfg.DataSettings.Interval) + log.Infof(common.Backtester, "fetching data for %v %v %v %v", exch.GetName(), a, fPair, cfg.DataSettings.Interval) loadNewDataTimer.Reset(time.Second * 15) err = bt.loadLiveData(resp, cfg, exch, fPair, a, dataType) if err != nil { - log.Error(common.SubLoggers[common.Backtester], err) + log.Error(common.Backtester, err) return } } @@ -123,7 +123,7 @@ func (bt *BackTest) loadLiveData(resp *kline.DataFromKline, cfg *config.Config, candles, err := live.LoadData(context.TODO(), exch, dataType, - cfg.DataSettings.Interval, + cfg.DataSettings.Interval.Duration(), fPair, a) if err != nil { @@ -134,6 +134,6 @@ func (bt *BackTest) loadLiveData(resp *kline.DataFromKline, cfg *config.Config, } resp.AppendResults(candles) bt.Reports.UpdateItem(&resp.Item) - log.Info(common.SubLoggers[common.Backtester], "sleeping for 30 seconds before checking for new candle data") + log.Info(common.Backtester, "sleeping for 30 seconds before checking for new candle data") return nil } diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index e877bee2c4f..5c9bda5bec2 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -43,7 +43,7 @@ import ( // NewFromConfig takes a strategy config and configures a backtester variable to run func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool) (*BackTest, error) { - log.Infoln(common.SubLoggers[common.Setup], "loading config...") + log.Infoln(common.Setup, "loading config...") if cfg == nil { return nil, errNilConfig } @@ -99,11 +99,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if cfg.FundingSettings.UseExchangeLevelFunding { for i := range cfg.FundingSettings.ExchangeLevelFunding { var a asset.Item - a, err = asset.New(cfg.FundingSettings.ExchangeLevelFunding[i].Asset) - if err != nil { - return nil, err - } - cq := currency.NewCode(cfg.FundingSettings.ExchangeLevelFunding[i].Currency) + a = cfg.FundingSettings.ExchangeLevelFunding[i].Asset + cq := cfg.FundingSettings.ExchangeLevelFunding[i].Currency var item *funding.Item item, err = funding.CreateItem(cfg.FundingSettings.ExchangeLevelFunding[i].ExchangeName, a, @@ -174,14 +171,15 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] = make(map[asset.Item]map[currency.Pair]*risk.CurrencySettings) } var a asset.Item - a, err = asset.New(cfg.CurrencySettings[i].Asset) + a = cfg.CurrencySettings[i].Asset if err != nil { return nil, fmt.Errorf( - "%w for %v %v %v. Err %v", + "%w for %v %v %v-%v. Err %v", errInvalidConfigAsset, cfg.CurrencySettings[i].ExchangeName, cfg.CurrencySettings[i].Asset, - cfg.CurrencySettings[i].Base+cfg.CurrencySettings[i].Quote, + cfg.CurrencySettings[i].Base, + cfg.CurrencySettings[i].Quote, err) } if portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName][a] == nil { @@ -189,8 +187,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool } var curr currency.Pair var b, q currency.Code - b = currency.NewCode(cfg.CurrencySettings[i].Base) - q = currency.NewCode(cfg.CurrencySettings[i].Quote) + b = cfg.CurrencySettings[i].Base + q = cfg.CurrencySettings[i].Quote curr = currency.NewPair(b, q) var exch gctexchange.IBotExchange exch, err = bt.exchangeManager.GetExchangeByName(cfg.CurrencySettings[i].ExchangeName) @@ -201,7 +199,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool var requestFormat currency.PairFormat requestFormat, err = exchBase.GetPairFormat(a, true) if err != nil { - return nil, fmt.Errorf("could not format currency %v, %w", curr, err) + return nil, fmt.Errorf("could not get pair format %v, %w", curr, err) } curr = curr.Format(requestFormat.Delimiter, requestFormat.Uppercase) var avail, enabled currency.Pairs @@ -236,7 +234,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if cfg.CurrencySettings[i].MakerFee != nil && cfg.CurrencySettings[i].TakerFee != nil && cfg.CurrencySettings[i].MakerFee.GreaterThan(*cfg.CurrencySettings[i].TakerFee) { - log.Warnf(common.SubLoggers[common.Setup], "maker fee '%v' should not exceed taker fee '%v'. Please review config", + log.Warnf(common.Setup, "maker fee '%v' should not exceed taker fee '%v'. Please review config", cfg.CurrencySettings[i].MakerFee, cfg.CurrencySettings[i].TakerFee) } @@ -364,7 +362,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool StrategyGoal: cfg.Goal, ExchangeAssetPairStatistics: make(map[string]map[asset.Item]map[currency.Pair]*statistics.CurrencyPairStatistic), RiskFreeRate: cfg.StatisticSettings.RiskFreeRate, - CandleInterval: gctkline.Interval(cfg.DataSettings.Interval), + CandleInterval: cfg.DataSettings.Interval, FundManager: bt.Funding, } bt.Statistic = stats @@ -389,8 +387,8 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool for j := range cfg.CurrencySettings { if cfg.CurrencySettings[j].ExchangeName == trackingPairs[i].Exchange && cfg.CurrencySettings[j].Asset == trackingPairs[i].Asset && - cfg.CurrencySettings[j].Base == trackingPairs[i].Base && - cfg.CurrencySettings[j].Quote == trackingPairs[i].Quote { + cfg.CurrencySettings[j].Base.Equal(trackingPairs[i].Base) && + cfg.CurrencySettings[j].Quote.Equal(trackingPairs[i].Quote) { continue trackingPairCheck } } @@ -424,7 +422,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool } func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange, error) { - log.Infoln(common.SubLoggers[common.Setup], "setting exchange settings...") + log.Infoln(common.Setup, "setting exchange settings...") resp := exchange.Exchange{} for i := range cfg.CurrencySettings { @@ -476,7 +474,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange } if cfg.CurrencySettings[i].MaximumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(common.SubLoggers[common.Setup], "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", + log.Warnf(common.Setup, "invalid maximum slippage percent '%v'. Slippage percent is defined as a number, eg '100.00', defaulting to '%v'", cfg.CurrencySettings[i].MaximumSlippagePercent, slippage.DefaultMaximumSlippagePercent) cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent @@ -485,7 +483,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange cfg.CurrencySettings[i].MaximumSlippagePercent = slippage.DefaultMaximumSlippagePercent } if cfg.CurrencySettings[i].MinimumSlippagePercent.LessThan(decimal.Zero) { - log.Warnf(common.SubLoggers[common.Setup], "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", + log.Warnf(common.Setup, "invalid minimum slippage percent '%v'. Slippage percent is defined as a number, eg '80.00', defaulting to '%v'", cfg.CurrencySettings[i].MinimumSlippagePercent, slippage.DefaultMinimumSlippagePercent) cfg.CurrencySettings[i].MinimumSlippagePercent = slippage.DefaultMinimumSlippagePercent @@ -520,7 +518,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange if limits != nil { if !cfg.CurrencySettings[i].CanUseExchangeLimits { - log.Warnf(common.SubLoggers[common.Setup], "exchange %s order execution limits supported but disabled for %s %s, live results may differ", + log.Warnf(common.Setup, "exchange %s order execution limits supported but disabled for %s %s, live results may differ", cfg.CurrencySettings[i].ExchangeName, pair, a) @@ -558,34 +556,25 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange return resp, nil } -func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { +func (bt *BackTest) loadExchangePairAssetBase(exch string, base, quote currency.Code, ai asset.Item) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { e, err := bt.exchangeManager.GetExchangeByName(exch) if err != nil { return nil, currency.EMPTYPAIR, "", err } var cp, fPair currency.Pair - cp, err = currency.NewPairFromStrings(base, quote) - if err != nil { - return nil, currency.EMPTYPAIR, "", err - } - - var a asset.Item - a, err = asset.New(ass) - if err != nil { - return nil, currency.EMPTYPAIR, "", err - } + cp = currency.NewPair(base, quote) exchangeBase := e.GetBase() if exchangeBase.ValidateAPICredentials(exchangeBase.GetDefaultCredentials()) != nil { - log.Warnf(common.SubLoggers[common.Setup], "no credentials set for %v, this is theoretical only", exchangeBase.Name) + log.Warnf(common.Setup, "no credentials set for %v, this is theoretical only", exchangeBase.Name) } - fPair, err = exchangeBase.FormatExchangeCurrency(cp, a) + fPair, err = exchangeBase.FormatExchangeCurrency(cp, ai) if err != nil { return nil, currency.EMPTYPAIR, "", err } - return e, fPair, a, nil + return e, fPair, ai, nil } // getFees will return an exchange's fee rate from GCT's wrapper function @@ -598,7 +587,7 @@ func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency. Amount: 1, }) if err != nil { - log.Errorf(common.SubLoggers[common.Setup], "Could not retrieve taker fee for %v. %v", exch.GetName(), err) + log.Errorf(common.Setup, "Could not retrieve taker fee for %v. %v", exch.GetName(), err) } fMakerFee, err := exch.GetFeeByType(ctx, @@ -610,7 +599,7 @@ func getFees(ctx context.Context, exch gctexchange.IBotExchange, fPair currency. Amount: 1, }) if err != nil { - log.Errorf(common.SubLoggers[common.Setup], "Could not retrieve maker fee for %v. %v", exch.GetName(), err) + log.Errorf(common.Setup, "Could not retrieve maker fee for %v. %v", exch.GetName(), err) } return decimal.NewFromFloat(fMakerFee), decimal.NewFromFloat(fTakerFee) @@ -643,7 +632,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, return nil, err } - log.Infof(common.SubLoggers[common.Setup], "loading data for %v %v %v...\n", exch.GetName(), a, fPair) + log.Infof(common.Setup, "loading data for %v %v %v...\n", exch.GetName(), a, fPair) resp := &kline.DataFromKline{} switch { case cfg.DataSettings.CSVData != nil: @@ -654,7 +643,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, dataType, cfg.DataSettings.CSVData.FullPath, strings.ToLower(exch.GetName()), - cfg.DataSettings.Interval, + cfg.DataSettings.Interval.Duration(), fPair, a, isUSDTrackingPair) @@ -665,8 +654,8 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, resp.Item.SortCandlesByTimestamp(false) resp.RangeHolder, err = gctkline.CalculateCandleDateRanges( resp.Item.Candles[0].Time, - resp.Item.Candles[len(resp.Item.Candles)-1].Time.Add(cfg.DataSettings.Interval), - gctkline.Interval(cfg.DataSettings.Interval), + resp.Item.Candles[len(resp.Item.Candles)-1].Time.Add(cfg.DataSettings.Interval.Duration()), + cfg.DataSettings.Interval, 0, ) if err != nil { @@ -675,11 +664,11 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) summary := resp.RangeHolder.DataSummary(false) if len(summary) > 0 { - log.Warnf(common.SubLoggers[common.Setup], "%v", summary) + log.Warnf(common.Setup, "%v", summary) } case cfg.DataSettings.DatabaseData != nil: if cfg.DataSettings.DatabaseData.InclusiveEndDate { - cfg.DataSettings.DatabaseData.EndDate = cfg.DataSettings.DatabaseData.EndDate.Add(cfg.DataSettings.Interval) + cfg.DataSettings.DatabaseData.EndDate = cfg.DataSettings.DatabaseData.EndDate.Add(cfg.DataSettings.Interval.Duration()) } if cfg.DataSettings.DatabaseData.Path == "" { cfg.DataSettings.DatabaseData.Path = filepath.Join(gctcommon.GetDefaultDataDir(runtime.GOOS), "database") @@ -696,7 +685,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, defer func() { stopErr := bt.databaseManager.Stop() if stopErr != nil { - log.Error(common.SubLoggers[common.Setup], stopErr) + log.Error(common.Setup, stopErr) } }() resp, err = loadDatabaseData(cfg, exch.GetName(), fPair, a, dataType, isUSDTrackingPair) @@ -709,7 +698,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, resp.RangeHolder, err = gctkline.CalculateCandleDateRanges( cfg.DataSettings.DatabaseData.StartDate, cfg.DataSettings.DatabaseData.EndDate, - gctkline.Interval(cfg.DataSettings.Interval), + cfg.DataSettings.Interval, 0, ) if err != nil { @@ -718,11 +707,11 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange, resp.RangeHolder.SetHasDataFromCandles(resp.Item.Candles) summary := resp.RangeHolder.DataSummary(false) if len(summary) > 0 { - log.Warnf(common.SubLoggers[common.Setup], "%v", summary) + log.Warnf(common.Setup, "%v", summary) } case cfg.DataSettings.APIData != nil: if cfg.DataSettings.APIData.InclusiveEndDate { - cfg.DataSettings.APIData.EndDate = cfg.DataSettings.APIData.EndDate.Add(cfg.DataSettings.Interval) + cfg.DataSettings.APIData.EndDate = cfg.DataSettings.APIData.EndDate.Add(cfg.DataSettings.Interval.Duration()) } resp, err = loadAPIData( cfg, @@ -799,7 +788,7 @@ func loadDatabaseData(cfg *config.Config, name string, fPair currency.Pair, a as return database.LoadData( cfg.DataSettings.DatabaseData.StartDate, cfg.DataSettings.DatabaseData.EndDate, - cfg.DataSettings.Interval, + cfg.DataSettings.Interval.Duration(), strings.ToLower(name), dataType, fPair, @@ -814,7 +803,7 @@ func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair curren dates, err := gctkline.CalculateCandleDateRanges( cfg.DataSettings.APIData.StartDate, cfg.DataSettings.APIData.EndDate, - gctkline.Interval(cfg.DataSettings.Interval), + cfg.DataSettings.Interval, resultLimit) if err != nil { return nil, err @@ -823,7 +812,7 @@ func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair curren dataType, cfg.DataSettings.APIData.StartDate, cfg.DataSettings.APIData.EndDate, - cfg.DataSettings.Interval, + cfg.DataSettings.Interval.Duration(), exch, fPair, a) @@ -833,7 +822,7 @@ func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair curren dates.SetHasDataFromCandles(candles.Candles) summary := dates.DataSummary(false) if len(summary) > 0 { - log.Warnf(common.SubLoggers[common.Setup], "%v", summary) + log.Warnf(common.Setup, "%v", summary) } candles.FillMissingDataWithEmptyEntries(dates) candles.RemoveOutsideRange(cfg.DataSettings.APIData.StartDate, cfg.DataSettings.APIData.EndDate) @@ -870,7 +859,7 @@ func loadLiveData(cfg *config.Config, base *gctexchange.Base) error { validated := base.AreCredentialsValid(context.TODO()) base.API.AuthenticatedSupport = validated if !validated && cfg.DataSettings.LiveData.RealOrders { - log.Warn(common.SubLoggers[common.Setup], "invalid API credentials set, real orders set to false") + log.Warn(common.Setup, "invalid API credentials set, real orders set to false") cfg.DataSettings.LiveData.RealOrders = false } return nil diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 04e68cd8f46..ca0b2e127fe 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -16,6 +16,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -279,13 +280,13 @@ func (p *Portfolio) OnFill(ev fill.Event, funds funding.IFundReleaser) (fill.Eve err = p.setHoldingsForOffset(&h, false) } if err != nil { - log.Error(common.SubLoggers[common.Portfolio], err) + log.Error(common.Portfolio, err) } } err = p.addComplianceSnapshot(ev) if err != nil { - log.Error(common.SubLoggers[common.Portfolio], err) + log.Error(common.Portfolio, err) } ev.SetExchangeFee(decimal.Zero) @@ -471,10 +472,6 @@ func (s *Settings) GetLatestHoldings() holdings.Holding { // GetHoldingsForTime returns the holdings for a time period, or an empty holding if not found func (s *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { - if s.HoldingsSnapshots == nil { - // no holdings yet - return holdings.Holding{} - } for i := len(s.HoldingsSnapshots) - 1; i >= 0; i-- { if s.HoldingsSnapshots[i].Timestamp.Equal(t) { return s.HoldingsSnapshots[i] @@ -483,8 +480,6 @@ func (s *Settings) GetHoldingsForTime(t time.Time) holdings.Holding { return holdings.Holding{} } -var errUnsetFuturesTracker = errors.New("portfolio settings futures tracker unset") - // GetPositions returns all futures positions for an event's exchange, asset, pair func (p *Portfolio) GetPositions(e common.EventHandler) ([]gctorder.PositionStats, error) { settings, err := p.getFuturesSettingsFromEvent(e) @@ -581,11 +576,7 @@ func (p *Portfolio) TrackFuturesOrder(ev fill.Event, fund funding.IFundReleaser) } } - pnl, err := p.GetLatestPNLForEvent(ev) - if err != nil { - return nil, err - } - return pnl, nil + return p.GetLatestPNLForEvent(ev) } // GetLatestPNLForEvent takes in an event and returns the latest PNL data @@ -632,8 +623,7 @@ func (p *Portfolio) CheckLiquidationStatus(ev common.DataEventHandler, collatera } if !position.Status.IsInactive() && pnl.Result.UnrealisedPNL.IsNegative() && - pnl.Result.UnrealisedPNL.Abs().GreaterThan(availableFunds) && - availableFunds.GreaterThan(decimal.Zero) { + pnl.Result.UnrealisedPNL.Abs().GreaterThan(availableFunds) { return gctorder.ErrPositionLiquidated } @@ -649,75 +639,73 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle return nil, fmt.Errorf("%w, requires funding manager", common.ErrNilArguments) } var closingOrders []order.Event - for exch, assetMap := range p.exchangeAssetPairSettings { - if !strings.EqualFold(ev.GetExchange(), exch) { - // only liquidate the same exchange - continue - } - for item, pairMap := range assetMap { - for pair, settings := range pairMap { - switch { - case item.IsFutures(): - positions := settings.FuturesTracker.GetPositions() - if len(positions) == 0 { + assetPairSettings, ok := p.exchangeAssetPairSettings[ev.GetExchange()] + if !ok { + return nil, config.ErrExchangeNotFound + } + for item, pairMap := range assetPairSettings { + for pair, settings := range pairMap { + switch { + case item.IsFutures(): + positions := settings.FuturesTracker.GetPositions() + if len(positions) == 0 { + continue + } + pos := positions[len(positions)-1] + if !pos.Exposure.IsPositive() { + continue + } + direction := gctorder.Short + if pos.LatestDirection == gctorder.Short { + direction = gctorder.Long + } + closingOrders = append(closingOrders, &order.Order{ + Base: event.Base{ + Offset: ev.GetOffset(), + Exchange: pos.Exchange, + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: pos.Pair, + UnderlyingPair: pos.Pair, + AssetType: pos.Asset, + Reason: "LIQUIDATED", + }, + Direction: direction, + Status: gctorder.Liquidated, + ClosePrice: ev.GetClosePrice(), + Amount: pos.Exposure, + AllocatedSize: pos.Exposure, + OrderType: gctorder.Market, + LiquidatingPosition: true, + }) + case item == asset.Spot: + allFunds := funds.GetAllFunding() + for i := range allFunds { + if allFunds[i].Asset.IsFutures() { continue } - pos := positions[len(positions)-1] - if !pos.Exposure.IsPositive() { + if allFunds[i].Currency.IsFiatCurrency() || allFunds[i].Currency.IsStableCurrency() { + // close orders for assets + // funding manager will zero for fiat/stable continue } - direction := gctorder.Short - if pos.LatestDirection == gctorder.Short { - direction = gctorder.Long - } closingOrders = append(closingOrders, &order.Order{ Base: event.Base{ - Offset: ev.GetOffset(), - Exchange: pos.Exchange, - Time: ev.GetTime(), - Interval: ev.GetInterval(), - CurrencyPair: pos.Pair, - UnderlyingPair: pos.Pair, - AssetType: pos.Asset, - Reason: "LIQUIDATED", + Offset: ev.GetOffset(), + Exchange: ev.GetExchange(), + Time: ev.GetTime(), + Interval: ev.GetInterval(), + CurrencyPair: pair, + AssetType: item, + Reason: "LIQUIDATED", }, - Direction: direction, + Direction: gctorder.Sell, Status: gctorder.Liquidated, - ClosePrice: ev.GetClosePrice(), - Amount: pos.Exposure, - AllocatedSize: pos.Exposure, + Amount: allFunds[i].Available, OrderType: gctorder.Market, + AllocatedSize: allFunds[i].Available, LiquidatingPosition: true, }) - case item == asset.Spot: - allFunds := funds.GetAllFunding() - for i := range allFunds { - if allFunds[i].Asset.IsFutures() { - continue - } - if allFunds[i].Currency.IsFiatCurrency() || allFunds[i].Currency.IsStableCurrency() { - // close orders for assets - // funding manager will zero for fiat/stable - continue - } - closingOrders = append(closingOrders, &order.Order{ - Base: event.Base{ - Offset: ev.GetOffset(), - Exchange: exch, - Time: ev.GetTime(), - Interval: ev.GetInterval(), - CurrencyPair: pair, - AssetType: item, - Reason: "LIQUIDATED", - }, - Direction: gctorder.Sell, - Status: gctorder.Liquidated, - Amount: allFunds[i].Available, - OrderType: gctorder.Market, - AllocatedSize: allFunds[i].Available, - LiquidatingPosition: true, - }) - } } } } @@ -754,23 +742,23 @@ func (p *Portfolio) getSettings(exch string, item asset.Item, pair currency.Pair if !ok { return nil, errAssetUnset } - pairMap, ok := itemMap[pair] + pairSettings, ok := itemMap[pair] if !ok { return nil, errCurrencyPairUnset } - return pairMap, nil + return pairSettings, nil } // GetLatestPNLs returns all PNL details in one array func (p *Portfolio) GetLatestPNLs() []PNLSummary { var result []PNLSummary - for exchK := range p.exchangeAssetPairSettings { - for assetK := range p.exchangeAssetPairSettings[exchK] { - if !assetK.IsFutures() { + for exch, assetPairSettings := range p.exchangeAssetPairSettings { + for ai, pairSettings := range assetPairSettings { + if !ai.IsFutures() { continue } - for pairK, settings := range p.exchangeAssetPairSettings[exchK][assetK] { + for cp, settings := range pairSettings { if settings == nil { continue } @@ -778,9 +766,9 @@ func (p *Portfolio) GetLatestPNLs() []PNLSummary { continue } summary := PNLSummary{ - Exchange: exchK, - Item: assetK, - Pair: pairK, + Exchange: exch, + Item: ai, + Pair: cp, } positions := settings.FuturesTracker.GetPositions() if len(positions) > 0 { diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 22fbf5e7df3..bec3089a92b 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -721,7 +721,10 @@ func TestCalculatePNL(t *testing.T) { exch := &ftx.FTX{} exch.Name = testExchange a := asset.Futures - pair, _ := currency.NewPairFromStrings("BTC", "1231") + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Errorf("received: %v, expected: %v", err, nil) + } err = p.SetupCurrencySettingsMap(&exchange.Settings{ Exchange: exch, UseRealOrders: false, diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index 409c3a0bbec..fb1d169a755 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -34,6 +34,7 @@ var ( errNoHoldings = errors.New("no holdings found") errHoldingsNoTimestamp = errors.New("holding with unset timestamp received") errHoldingsAlreadySet = errors.New("holding already set") + errUnsetFuturesTracker = errors.New("portfolio settings futures tracker unset") ) // Portfolio stores all holdings and rules to assess orders, allowing the portfolio manager to diff --git a/backtester/eventhandlers/portfolio/setup.go b/backtester/eventhandlers/portfolio/setup.go index a4f42bdf5fa..6d13046680d 100644 --- a/backtester/eventhandlers/portfolio/setup.go +++ b/backtester/eventhandlers/portfolio/setup.go @@ -68,14 +68,12 @@ func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { return err } settings := &Settings{ - Fee: setup.ExchangeFee, - BuySideSizing: setup.BuySide, - SellSideSizing: setup.SellSide, - Leverage: setup.Leverage, - ComplianceManager: compliance.Manager{ - Snapshots: []compliance.Snapshot{}, - }, - Exchange: setup.Exchange, + Fee: setup.ExchangeFee, + BuySideSizing: setup.BuySide, + SellSideSizing: setup.SellSide, + Leverage: setup.Leverage, + Exchange: setup.Exchange, + ComplianceManager: compliance.Manager{}, } if setup.Asset.IsFutures() { futureTrackerSetup := &gctorder.MultiPositionTrackerSetup{ diff --git a/backtester/eventhandlers/statistics/common.go b/backtester/eventhandlers/statistics/common.go index 1171f5cefb7..28208b3df0f 100644 --- a/backtester/eventhandlers/statistics/common.go +++ b/backtester/eventhandlers/statistics/common.go @@ -45,8 +45,7 @@ func CalculateBiggestEventDrawdown(closePrices []common.DataEventHandler) (Swing } intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[i].GetInterval(), 0) if err != nil { - log.Error(common.SubLoggers[common.CurrencyStatistics], err) - continue + return Swing{}, fmt.Errorf("cannot calculate max drawdown, date range error: %w", err) } if highestPrice.IsPositive() && lowestPrice.IsPositive() { swings = append(swings, Swing{ @@ -77,7 +76,7 @@ func CalculateBiggestEventDrawdown(closePrices []common.DataEventHandler) (Swing } intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, closePrices[0].GetInterval(), 0) if err != nil { - return Swing{}, err + return Swing{}, fmt.Errorf("cannot close out max drawdown calculation: %w", err) } drawdownPercent := decimal.Zero if highestPrice.GreaterThan(decimal.Zero) { @@ -169,7 +168,7 @@ func CalculateBiggestValueAtTimeDrawdown(closePrices []ValueAtTime, interval gct } intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0) if err != nil { - log.Error(common.SubLoggers[common.CurrencyStatistics], err) + log.Error(common.CurrencyStatistics, err) } drawdownPercent := decimal.Zero if highestPrice.GreaterThan(decimal.Zero) { @@ -239,7 +238,7 @@ func CalculateRatios(benchmarkRates, returnsPerCandle []decimal.Decimal, riskFre arithmeticSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, arithmeticReturnsPerCandle) if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { if errors.Is(err, gctmath.ErrInexactConversion) { - log.Warnf(common.SubLoggers[common.Statistics], "%s funding arithmetic sortino ratio %v", logMessage, err) + log.Warnf(common.Statistics, "%s funding arithmetic sortino ratio %v", logMessage, err) } else { return nil, nil, err } @@ -274,7 +273,7 @@ func CalculateRatios(benchmarkRates, returnsPerCandle []decimal.Decimal, riskFre geomSortino, err = gctmath.DecimalSortinoRatio(returnsPerCandle, riskFreeRatePerCandle, geometricReturnsPerCandle) if err != nil && !errors.Is(err, gctmath.ErrNoNegativeResults) { if errors.Is(err, gctmath.ErrInexactConversion) { - log.Warnf(common.SubLoggers[common.Statistics], "%s geometric sortino ratio %v", logMessage, err) + log.Warnf(common.Statistics, "%s geometric sortino ratio %v", logMessage, err) } else { return nil, nil, err } diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index bd6d4cff967..85d4e5ed736 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -5,7 +5,6 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" - "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio" gctcommon "github.com/thrasher-corp/gocryptotrader/common" gctmath "github.com/thrasher-corp/gocryptotrader/common/math" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -149,9 +148,8 @@ func (c *CurrencyPairStatistic) analysePNLGrowth() { if c.Events[i].PNL == nil { continue } - var unrealised, realised portfolio.BasicPNLResult - unrealised = c.Events[i].PNL.GetUnrealisedPNL() - realised = c.Events[i].PNL.GetRealisedPNL() + unrealised := c.Events[i].PNL.GetUnrealisedPNL() + realised := c.Events[i].PNL.GetRealisedPNL() if unrealised.PNL.LessThan(lowestUnrealised.Value) || !lowestUnrealised.Set { lowestUnrealised.Value = unrealised.PNL lowestUnrealised.Time = unrealised.Time diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index f853c769186..de6e2310388 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -23,41 +23,41 @@ const ( // addReason basic helper to append event reason if one is there func addReason(reason, msg string) string { if reason != "" { - msg = msg + "\tReason: " + reason + msg += "\tReason: " + reason } return msg } // PrintTotalResults outputs all results to the CMD func (s *Statistic) PrintTotalResults() { - log.Info(common.SubLoggers[common.Statistics], common.ColourH1+"------------------Strategy-----------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Statistics], "Strategy Name: %v", s.StrategyName) - log.Infof(common.SubLoggers[common.Statistics], "Strategy Nickname: %v", s.StrategyNickname) - log.Infof(common.SubLoggers[common.Statistics], "Strategy Goal: %v\n\n", s.StrategyGoal) + log.Info(common.Statistics, common.ColourH1+"------------------Strategy-----------------------------------"+common.ColourDefault) + log.Infof(common.Statistics, "Strategy Name: %v", s.StrategyName) + log.Infof(common.Statistics, "Strategy Nickname: %v", s.StrategyNickname) + log.Infof(common.Statistics, "Strategy Goal: %v\n\n", s.StrategyGoal) - log.Info(common.SubLoggers[common.Statistics], common.ColourH2+"------------------Total Results------------------------------"+common.ColourDefault) - log.Info(common.SubLoggers[common.Statistics], common.ColourH3+"------------------Orders-------------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Statistics], "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) - log.Infof(common.SubLoggers[common.Statistics], "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) - log.Infof(common.SubLoggers[common.Statistics], "Total long orders: %v", convert.IntToHumanFriendlyString(s.TotalLongOrders, ",")) - log.Infof(common.SubLoggers[common.Statistics], "Total short orders: %v", convert.IntToHumanFriendlyString(s.TotalShortOrders, ",")) - log.Infof(common.SubLoggers[common.Statistics], "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) + log.Info(common.Statistics, common.ColourH2+"------------------Total Results------------------------------"+common.ColourDefault) + log.Info(common.Statistics, common.ColourH3+"------------------Orders-------------------------------------"+common.ColourDefault) + log.Infof(common.Statistics, "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ",")) + log.Infof(common.Statistics, "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ",")) + log.Infof(common.Statistics, "Total long orders: %v", convert.IntToHumanFriendlyString(s.TotalLongOrders, ",")) + log.Infof(common.Statistics, "Total short orders: %v", convert.IntToHumanFriendlyString(s.TotalShortOrders, ",")) + log.Infof(common.Statistics, "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ",")) if s.BiggestDrawdown != nil { - log.Info(common.SubLoggers[common.Statistics], common.ColourH3+"------------------Biggest Drawdown-----------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Statistics], "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair) - log.Infof(common.SubLoggers[common.Statistics], "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time) - log.Infof(common.SubLoggers[common.Statistics], "Lowest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Lowest.Value, 8, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Lowest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Lowest.Time) - log.Infof(common.SubLoggers[common.Statistics], "Calculated Drawdown: %s%%", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.DrawdownPercent, 2, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Difference: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value.Sub(s.BiggestDrawdown.MaxDrawdown.Lowest.Value), 8, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Drawdown length: %v\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ",")) + log.Info(common.Statistics, common.ColourH3+"------------------Biggest Drawdown-----------------------"+common.ColourDefault) + log.Infof(common.Statistics, "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair) + log.Infof(common.Statistics, "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ",")) + log.Infof(common.Statistics, "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time) + log.Infof(common.Statistics, "Lowest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Lowest.Value, 8, ".", ",")) + log.Infof(common.Statistics, "Lowest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Lowest.Time) + log.Infof(common.Statistics, "Calculated Drawdown: %s%%", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.DrawdownPercent, 2, ".", ",")) + log.Infof(common.Statistics, "Difference: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value.Sub(s.BiggestDrawdown.MaxDrawdown.Lowest.Value), 8, ".", ",")) + log.Infof(common.Statistics, "Drawdown length: %v candles\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ",")) } if s.BestMarketMovement != nil && s.BestStrategyResults != nil { - log.Info(common.SubLoggers[common.Statistics], common.ColourH4+"------------------Orders----------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.Statistics], "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ",")) - log.Infof(common.SubLoggers[common.Statistics], "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ",")) + log.Info(common.Statistics, common.ColourH4+"------------------Orders----------------------------------"+common.ColourDefault) + log.Infof(common.Statistics, "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ",")) + log.Infof(common.Statistics, "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ",")) } } @@ -66,7 +66,7 @@ func (s *Statistic) PrintTotalResults() { // grouped by time to allow a clearer picture of events func (s *Statistic) PrintAllEventsChronologically() { var results []eventOutputHolder - log.Info(common.SubLoggers[common.Statistics], common.ColourH1+"------------------Events-------------------------------------"+common.ColourDefault) + log.Info(common.Statistics, common.ColourH1+"------------------Events-------------------------------------"+common.ColourDefault) var errs gctcommon.Errors colour := common.ColourDefault for exch, x := range s.ExchangeAssetPairStatistics { @@ -152,13 +152,13 @@ func (s *Statistic) PrintAllEventsChronologically() { }) for i := range results { for j := range results[i].Events { - log.Info(common.SubLoggers[common.Statistics], results[i].Events[j]) + log.Info(common.Statistics, results[i].Events[j]) } } if len(errs) > 0 { - log.Info(common.SubLoggers[common.Statistics], common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) + log.Info(common.Statistics, common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) for i := range errs { - log.Error(common.SubLoggers[common.Statistics], errs[i].Error()) + log.Error(common.Statistics, errs[i].Error()) } } } @@ -179,92 +179,92 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. last.Holdings.TotalValueLost = last.Holdings.TotalValueLostToSlippage.Add(last.Holdings.TotalValueLostToVolumeSizing) sep := fmt.Sprintf("%v %v %v |\t", fSIL(e, limit12), fSIL(a.String(), limit10), fSIL(p.String(), limit14)) currStr := fmt.Sprintf(common.ColourH1+"------------------Stats for %v %v %v------------------------------------------------------"+common.ColourDefault, e, a, p) - log.Infof(common.SubLoggers[common.CurrencyStatistics], currStr[:70]) + log.Infof(common.CurrencyStatistics, currStr[:70]) if a.IsFutures() { - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Long orders: %s", sep, convert.IntToHumanFriendlyString(c.LongOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Short orders: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestUnrealisedPNL.Value, 8, ".", ","), c.HighestUnrealisedPNL.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestUnrealisedPNL.Value, 8, ".", ","), c.LowestUnrealisedPNL.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestRealisedPNL.Value, 8, ".", ","), c.HighestRealisedPNL.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestRealisedPNL.Value, 8, ".", ","), c.LowestRealisedPNL.Time) + log.Infof(common.CurrencyStatistics, "%s Long orders: %s", sep, convert.IntToHumanFriendlyString(c.LongOrders, ",")) + log.Infof(common.CurrencyStatistics, "%s Short orders: %s", sep, convert.IntToHumanFriendlyString(c.ShortOrders, ",")) + log.Infof(common.CurrencyStatistics, "%s Highest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestUnrealisedPNL.Value, 8, ".", ","), c.HighestUnrealisedPNL.Time) + log.Infof(common.CurrencyStatistics, "%s Lowest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestUnrealisedPNL.Value, 8, ".", ","), c.LowestUnrealisedPNL.Time) + log.Infof(common.CurrencyStatistics, "%s Highest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestRealisedPNL.Value, 8, ".", ","), c.HighestRealisedPNL.Time) + log.Infof(common.CurrencyStatistics, "%s Lowest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestRealisedPNL.Value, 8, ".", ","), c.LowestRealisedPNL.Time) } else { - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ","), last.Holdings.Timestamp) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ","), last.Holdings.Timestamp) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) + log.Infof(common.CurrencyStatistics, "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) + log.Infof(common.CurrencyStatistics, "%s Buy value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ","), last.Holdings.Timestamp) + log.Infof(common.CurrencyStatistics, "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) + log.Infof(common.CurrencyStatistics, "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) + log.Infof(common.CurrencyStatistics, "%s Sell value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ","), last.Holdings.Timestamp) + log.Infof(common.CurrencyStatistics, "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) } - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) + log.Infof(common.CurrencyStatistics, "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Max Drawdown-------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Drawdown length: %s", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ",")) + log.Info(common.CurrencyStatistics, common.ColourH2+"------------------Max Drawdown-------------------------------"+common.ColourDefault) + log.Infof(common.CurrencyStatistics, "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time) + log.Infof(common.CurrencyStatistics, "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time) + log.Infof(common.CurrencyStatistics, "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Drawdown length: %s", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ",")) if !usingExchangeLevelFunding { - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Ratios------------------------------------------------"+common.ColourDefault) - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH3+"------------------Rates-------------------------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ",")) - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH4+"------------------Arithmetic--------------------------------------------"+common.ColourDefault) + log.Info(common.CurrencyStatistics, common.ColourH2+"------------------Ratios------------------------------------------------"+common.ColourDefault) + log.Info(common.CurrencyStatistics, common.ColourH3+"------------------Rates-------------------------------------------------"+common.ColourDefault) + log.Infof(common.CurrencyStatistics, "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ",")) + log.Info(common.CurrencyStatistics, common.ColourH4+"------------------Arithmetic--------------------------------------------"+common.ColourDefault) if c.ShowMissingDataWarning { - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") + log.Infoln(common.CurrencyStatistics, "Missing data was detected during this backtesting run") + log.Infoln(common.CurrencyStatistics, "Ratio calculations will be skewed") } - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.ArithmeticRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.ArithmeticRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Sharpe ratio: %v", sep, c.ArithmeticRatios.SharpeRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Sortino ratio: %v", sep, c.ArithmeticRatios.SortinoRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4)) - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH4+"------------------Geometric--------------------------------------------"+common.ColourDefault) + log.Info(common.CurrencyStatistics, common.ColourH4+"------------------Geometric--------------------------------------------"+common.ColourDefault) if c.ShowMissingDataWarning { - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.CurrencyStatistics], "Ratio calculations will be skewed") + log.Infoln(common.CurrencyStatistics, "Missing data was detected during this backtesting run") + log.Infoln(common.CurrencyStatistics, "Ratio calculations will be skewed") } - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sharpe ratio: %v", sep, c.GeometricRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Sortino ratio: %v", sep, c.GeometricRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Information ratio: %v", sep, c.GeometricRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Calmar ratio: %v", sep, c.GeometricRatios.CalmarRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Sharpe ratio: %v", sep, c.GeometricRatios.SharpeRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Sortino ratio: %v", sep, c.GeometricRatios.SortinoRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Information ratio: %v", sep, c.GeometricRatios.InformationRatio.Round(4)) + log.Infof(common.CurrencyStatistics, "%s Calmar ratio: %v", sep, c.GeometricRatios.CalmarRatio.Round(4)) } - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourH2+"------------------Results------------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Starting Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice.Value, 8, ".", ","), c.StartingClosePrice.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Finishing Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice.Value, 8, ".", ","), c.EndingClosePrice.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Lowest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice.Value, 8, ".", ","), c.LowestClosePrice.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Highest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice.Value, 8, ".", ","), c.HighestClosePrice.Time) + log.Info(common.CurrencyStatistics, common.ColourH2+"------------------Results------------------------------------"+common.ColourDefault) + log.Infof(common.CurrencyStatistics, "%s Starting Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice.Value, 8, ".", ","), c.StartingClosePrice.Time) + log.Infof(common.CurrencyStatistics, "%s Finishing Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice.Value, 8, ".", ","), c.EndingClosePrice.Time) + log.Infof(common.CurrencyStatistics, "%s Lowest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice.Value, 8, ".", ","), c.LowestClosePrice.Time) + log.Infof(common.CurrencyStatistics, "%s Highest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestClosePrice.Value, 8, ".", ","), c.HighestClosePrice.Time) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Market movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MarketMovement, 2, ".", ",")) if !usingExchangeLevelFunding { - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Strategy movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.StrategyMovement, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Did it beat the market: %v", sep, c.StrategyMovement.GreaterThan(c.MarketMovement)) + log.Infof(common.CurrencyStatistics, "%s Strategy movement: %s%%", sep, convert.DecimalToHumanFriendlyString(c.StrategyMovement, 2, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Did it beat the market: %v", sep, c.StrategyMovement.GreaterThan(c.MarketMovement)) } - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to volume sizing: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToVolumeSizing, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Value lost to slippage: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToSlippage, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Value lost: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLost, 2, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Total Fees: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalFees, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings value: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalAssetValue, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Value lost to volume sizing: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToVolumeSizing, 2, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Value lost to slippage: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLostToSlippage, 2, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Total Value lost: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalValueLost, 2, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Total Fees: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalFees, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Final holdings value: %s", sep, convert.DecimalToHumanFriendlyString(c.TotalAssetValue, 8, ".", ",")) if !usingExchangeLevelFunding { // the following have no direct translation to individual exchange level funds as they // combine base and quote values - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.QuoteSize, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final total value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.QuoteSize, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Final holdings: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BaseSize, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Final total value: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.TotalValue, 8, ".", ",")) } if last.PNL != nil { var unrealised, realised portfolio.BasicPNLResult unrealised = last.PNL.GetUnrealisedPNL() realised = last.PNL.GetRealisedPNL() - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Unrealised PNL: %s", sep, convert.DecimalToHumanFriendlyString(unrealised.PNL, 8, ".", ",")) - log.Infof(common.SubLoggers[common.CurrencyStatistics], "%s Final Realised PNL: %s", sep, convert.DecimalToHumanFriendlyString(realised.PNL, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Final Unrealised PNL: %s", sep, convert.DecimalToHumanFriendlyString(unrealised.PNL, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Final Realised PNL: %s", sep, convert.DecimalToHumanFriendlyString(realised.PNL, 8, ".", ",")) } if len(errs) > 0 { - log.Info(common.SubLoggers[common.CurrencyStatistics], common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) + log.Info(common.CurrencyStatistics, common.ColourError+"------------------Errors-------------------------------------"+common.ColourDefault) for i := range errs { - log.Error(common.SubLoggers[common.CurrencyStatistics], errs[i].Error()) + log.Error(common.CurrencyStatistics, errs[i].Error()) } } } @@ -283,105 +283,105 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { } } if len(spotResults) > 0 || len(futuresResults) > 0 { - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH1+"------------------Funding------------------------------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH1+"------------------Funding------------------------------------"+common.ColourDefault) } if len(spotResults) > 0 { - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH2+"------------------Funding Spot Item Results------------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH2+"------------------Funding Spot Item Results------------------"+common.ColourDefault) for i := range spotResults { sep := fmt.Sprintf("%v%v%v| ", fSIL(spotResults[i].ReportItem.Exchange, limit12), fSIL(spotResults[i].ReportItem.Asset.String(), limit10), fSIL(spotResults[i].ReportItem.Currency.String(), limit14)) if !spotResults[i].ReportItem.PairedWith.IsEmpty() { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Paired with: %v", sep, spotResults[i].ReportItem.PairedWith) + log.Infof(common.FundingStatistics, "%s Paired with: %v", sep, spotResults[i].ReportItem.PairedWith) } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.InitialFunds, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.FinalFunds, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Initial funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.InitialFunds, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Final funds: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.FinalFunds, 8, ".", ",")) if !f.Report.DisableUSDTracking && f.Report.UsingExchangeLevelFunding { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDInitialFunds, 2, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDFinalFunds, 2, ".", ",")) + log.Infof(common.FundingStatistics, "%s Initial funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDInitialFunds, 2, ".", ",")) + log.Infof(common.FundingStatistics, "%s Final funds in USD: $%s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.USDFinalFunds, 2, ".", ",")) } if spotResults[i].ReportItem.ShowInfinite { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: ∞%%", sep) + log.Infof(common.FundingStatistics, "%s Difference: ∞%%", sep) } else { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.Difference, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Difference: %s%%", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.Difference, 8, ".", ",")) } if spotResults[i].ReportItem.TransferFee.GreaterThan(decimal.Zero) { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.TransferFee, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.TransferFee, 8, ".", ",")) } if i != len(spotResults)-1 { - log.Info(common.SubLoggers[common.FundingStatistics], "") + log.Info(common.FundingStatistics, "") } } } if len(futuresResults) > 0 { - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH2+"------------------Funding Futures Item Results---------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH2+"------------------Funding Futures Item Results---------------"+common.ColourDefault) for i := range futuresResults { sep := fmt.Sprintf("%v%v%v| ", fSIL(futuresResults[i].ReportItem.Exchange, limit12), fSIL(futuresResults[i].ReportItem.Asset.String(), limit10), fSIL(futuresResults[i].ReportItem.Currency.String(), limit14)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Is Collateral: %v", sep, futuresResults[i].IsCollateral) + log.Infof(common.FundingStatistics, "%s Is Collateral: %v", sep, futuresResults[i].IsCollateral) if futuresResults[i].IsCollateral { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial Collateral: %v %v at %v", sep, futuresResults[i].InitialCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialCollateral.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Collateral: %v %v at %v", sep, futuresResults[i].FinalCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalCollateral.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].LowestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestCollateral.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Collateral: %v %v at %v", sep, futuresResults[i].HighestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestCollateral.Time) + log.Infof(common.FundingStatistics, "%s Initial Collateral: %v %v at %v", sep, futuresResults[i].InitialCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialCollateral.Time) + log.Infof(common.FundingStatistics, "%s Final Collateral: %v %v at %v", sep, futuresResults[i].FinalCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalCollateral.Time) + log.Infof(common.FundingStatistics, "%s Lowest Collateral: %v %v at %v", sep, futuresResults[i].LowestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestCollateral.Time) + log.Infof(common.FundingStatistics, "%s Highest Collateral: %v %v at %v", sep, futuresResults[i].HighestCollateral.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestCollateral.Time) } else { if !futuresResults[i].ReportItem.PairedWith.IsEmpty() { - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Collateral currency: %v", sep, futuresResults[i].ReportItem.PairedWith) + log.Infof(common.FundingStatistics, "%s Collateral currency: %v", sep, futuresResults[i].ReportItem.PairedWith) } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest Contract Holdings: %v %v at %v", sep, futuresResults[i].LowestHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestHoldings.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest Contract Holdings: %v %v at %v", sep, futuresResults[i].HighestHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestHoldings.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial Contract Holdings: %v %v at %v", sep, futuresResults[i].InitialHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialHoldings.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final Contract Holdings: %v %v at %v", sep, futuresResults[i].FinalHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalHoldings.Time) + log.Infof(common.FundingStatistics, "%s Lowest Contract Holdings: %v %v at %v", sep, futuresResults[i].LowestHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].LowestHoldings.Time) + log.Infof(common.FundingStatistics, "%s Highest Contract Holdings: %v %v at %v", sep, futuresResults[i].HighestHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].HighestHoldings.Time) + log.Infof(common.FundingStatistics, "%s Initial Contract Holdings: %v %v at %v", sep, futuresResults[i].InitialHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].InitialHoldings.Time) + log.Infof(common.FundingStatistics, "%s Final Contract Holdings: %v %v at %v", sep, futuresResults[i].FinalHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalHoldings.Time) } if i != len(futuresResults)-1 { - log.Info(common.SubLoggers[common.FundingStatistics], "") + log.Info(common.FundingStatistics, "") } } } if f.Report.DisableUSDTracking { return nil } - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH2+"------------------USD Tracking Totals------------------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH2+"------------------USD Tracking Totals------------------------"+common.ColourDefault) sep := "USD Tracking Total |\t" - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Long Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.LongOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Short Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.ShortOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) + log.Infof(common.FundingStatistics, "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) + log.Infof(common.FundingStatistics, "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) + log.Infof(common.FundingStatistics, "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) + log.Infof(common.FundingStatistics, "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) + log.Infof(common.FundingStatistics, "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) + log.Infof(common.FundingStatistics, "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) + log.Infof(common.FundingStatistics, "%s Long Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.LongOrders, ",")) + log.Infof(common.FundingStatistics, "%s Short Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.ShortOrders, ",")) + log.Infof(common.FundingStatistics, "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) + log.Infof(common.FundingStatistics, "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) + log.Infof(common.FundingStatistics, "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH3+"------------------Ratios------------------------------------------------"+common.ColourDefault) - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH4+"------------------Rates-------------------------------------------------"+common.ColourDefault) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ",")) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ",")) + log.Info(common.FundingStatistics, common.ColourH3+"------------------Ratios------------------------------------------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH4+"------------------Rates-------------------------------------------------"+common.ColourDefault) + log.Infof(common.FundingStatistics, "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ",")) + log.Infof(common.FundingStatistics, "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ",")) if f.TotalUSDStatistics.ArithmeticRatios == nil || f.TotalUSDStatistics.GeometricRatios == nil { return fmt.Errorf("%w missing ratio calculations", common.ErrNilArguments) } - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH4+"------------------Arithmetic--------------------------------------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH4+"------------------Arithmetic--------------------------------------------"+common.ColourDefault) if wasAnyDataMissing { - log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") + log.Infoln(common.FundingStatistics, "Missing data was detected during this backtesting run") + log.Infoln(common.FundingStatistics, "Ratio calculations will be skewed") } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SharpeRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.SortinoRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4)) - log.Info(common.SubLoggers[common.FundingStatistics], common.ColourH4+"------------------Geometric--------------------------------------------"+common.ColourDefault) + log.Info(common.FundingStatistics, common.ColourH4+"------------------Geometric--------------------------------------------"+common.ColourDefault) if wasAnyDataMissing { - log.Infoln(common.SubLoggers[common.FundingStatistics], "Missing data was detected during this backtesting run") - log.Infoln(common.SubLoggers[common.FundingStatistics], "Ratio calculations will be skewed") + log.Infoln(common.FundingStatistics, "Missing data was detected during this backtesting run") + log.Infoln(common.FundingStatistics, "Ratio calculations will be skewed") } - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SharpeRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SortinoRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Information ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.InformationRatio.Round(4)) - log.Infof(common.SubLoggers[common.FundingStatistics], "%s Calmar ratio: %v\n\n", sep, f.TotalUSDStatistics.GeometricRatios.CalmarRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Sharpe ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SharpeRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Sortino ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.SortinoRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Information ratio: %v", sep, f.TotalUSDStatistics.GeometricRatios.InformationRatio.Round(4)) + log.Infof(common.FundingStatistics, "%s Calmar ratio: %v\n\n", sep, f.TotalUSDStatistics.GeometricRatios.CalmarRatio.Round(4)) return nil } diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 495e08f7aa1..3217a676196 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -182,7 +182,7 @@ func (s *Statistic) AddComplianceSnapshotForTime(c compliance.Snapshot, e fill.E // CalculateAllResults calculates the statistics of all exchange asset pair holdings, // orders, ratios and drawdowns func (s *Statistic) CalculateAllResults() error { - log.Info(common.SubLoggers[common.Statistics], "calculating backtesting results") + log.Info(common.Statistics, "calculating backtesting results") s.PrintAllEventsChronologically() currCount := 0 var finalResults []FinalResultsHolder @@ -197,7 +197,7 @@ func (s *Statistic) CalculateAllResults() error { } err = stats.CalculateResults(s.RiskFreeRate) if err != nil { - log.Error(common.SubLoggers[common.Statistics], err) + log.Error(common.Statistics, err) } stats.PrintResults(exchangeName, assetItem, pair, s.FundManager.IsUsingExchangeLevelFunding()) stats.FinalHoldings = last.Holdings diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index a9214e2a196..60b9f2f38ea 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -15,6 +15,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -812,7 +813,7 @@ func TestCalculateTheResults(t *testing.T) { } } -func TestCalculateMaxDrawdown(t *testing.T) { +func TestCalculateBiggestEventDrawdown(t *testing.T) { tt1 := time.Now().Add(-gctkline.OneDay.Duration() * 7).Round(gctkline.OneDay.Duration()) exch := testExchange a := asset.Spot @@ -902,6 +903,24 @@ func TestCalculateMaxDrawdown(t *testing.T) { if resp.Highest.Value != decimal.NewFromInt(1337) && !resp.Lowest.Value.Equal(decimal.NewFromInt(1238)) { t.Error("unexpected max drawdown") } + + // bogus scenario + bogusEvent := []common.DataEventHandler{ + &kline.Kline{ + Base: event.Base{ + Exchange: exch, + CurrencyPair: p, + AssetType: a, + }, + Close: decimal.NewFromInt(1339), + High: decimal.NewFromInt(1339), + Low: decimal.NewFromInt(1339), + }, + } + resp, err = CalculateBiggestEventDrawdown(bogusEvent) + if !errors.Is(err, gctcommon.ErrDateUnset) { + t.Errorf("received %v expected %v", err, gctcommon.ErrDateUnset) + } } func TestCalculateBiggestValueAtTimeDrawdown(t *testing.T) { diff --git a/backtester/funding/trackingcurrencies/trackingcurrencies.go b/backtester/funding/trackingcurrencies/trackingcurrencies.go index 4192d61c18f..0d85e31c966 100644 --- a/backtester/funding/trackingcurrencies/trackingcurrencies.go +++ b/backtester/funding/trackingcurrencies/trackingcurrencies.go @@ -41,9 +41,9 @@ var rankedUSDs = []currency.Code{ // a USD equivalent type TrackingPair struct { Exchange string - Asset string - Base string - Quote string + Asset asset.Item + Base currency.Code + Quote currency.Code } // CreateUSDTrackingPairs is responsible for loading exchanges, @@ -63,15 +63,12 @@ func CreateUSDTrackingPairs(tp []TrackingPair, em *engine.ExchangeManager) ([]Tr if err != nil { return nil, err } - pair, err := currency.NewPairFromStrings(tp[i].Base, tp[i].Quote) - if err != nil { - return nil, err - } + pair := currency.NewPair(tp[i].Base, tp[i].Quote) if pairContainsUSD(pair) { resp = append(resp, tp[i]) } else { b := exch.GetBase() - a, err := asset.New(tp[i].Asset) + a := tp[i].Asset if err != nil { return nil, err } @@ -89,14 +86,14 @@ func CreateUSDTrackingPairs(tp []TrackingPair, em *engine.ExchangeManager) ([]Tr TrackingPair{ Exchange: tp[i].Exchange, Asset: tp[i].Asset, - Base: basePair.Base.String(), - Quote: basePair.Quote.String(), + Base: basePair.Base, + Quote: basePair.Quote, }, TrackingPair{ Exchange: tp[i].Exchange, Asset: tp[i].Asset, - Base: quotePair.Base.String(), - Quote: quotePair.Quote.String(), + Base: quotePair.Base, + Quote: quotePair.Quote, }, ) } diff --git a/backtester/main.go b/backtester/main.go index 6178abf70ca..fa0688e5833 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -1,12 +1,10 @@ package main import ( - "errors" "flag" "fmt" "os" "path/filepath" - "strings" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/config" @@ -93,7 +91,6 @@ func main() { common.ColourInfo = "" common.ColourDebug = "" common.ColourWarn = "" - common.ColourProblem = "" common.ColourDarkGrey = "" common.ColourError = "" } @@ -111,16 +108,10 @@ func main() { os.Exit(1) } - for k := range common.SubLoggers { - common.SubLoggers[k], err = log.NewSubLogger(k) - if err != nil { - if errors.Is(err, log.ErrSubLoggerAlreadyRegistered) { - common.SubLoggers[k] = log.SubLoggers[strings.ToUpper(k)] - continue - } - fmt.Printf("Could not setup global logger. Error: %v.\n", err) - os.Exit(1) - } + err = common.RegisterBacktesterSubLoggers() + if err != nil { + fmt.Printf("Could not register subloggers. Error: %v.\n", err) + os.Exit(1) } cfg, err = config.ReadConfigFromFile(configPath) @@ -157,11 +148,7 @@ func main() { log.Infof(log.Global, "Captured %v, shutdown requested.\n", interrupt) bt.Stop() } else { - err = bt.Run() - if err != nil { - fmt.Printf("Could not complete run. Error: %v.\n", err) - os.Exit(1) - } + bt.Run() } err = bt.Statistic.CalculateAllResults() diff --git a/backtester/report/report.go b/backtester/report/report.go index 1170ce19aa9..e1d519b1f21 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -18,7 +18,7 @@ import ( // GenerateReport sends final data from statistics to a template // to create a lovely final report for someone to view func (d *Data) GenerateReport() error { - log.Info(common.SubLoggers[common.Report], "generating report") + log.Info(common.Report, "generating report") err := d.enhanceCandles() if err != nil { return err @@ -90,7 +90,7 @@ func (d *Data) GenerateReport() error { defer func() { err = f.Close() if err != nil { - log.Error(common.SubLoggers[common.Report], err) + log.Error(common.Report, err) } }() @@ -98,7 +98,7 @@ func (d *Data) GenerateReport() error { if err != nil { return err } - log.Infof(common.SubLoggers[common.Report], "successfully saved report to %v", filepath.Join(d.OutputPath, fileName)) + log.Infof(common.Report, "successfully saved report to %v", filepath.Join(d.OutputPath, fileName)) return nil } diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index bf19ba7cadb..71f84f20eb4 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -2,9 +2,6 @@ package report import ( "errors" - "io/ioutil" - "os" - "path/filepath" "testing" "time" @@ -26,19 +23,9 @@ func TestGenerateReport(t *testing.T) { e := testExchange a := asset.Spot p := currency.NewPair(currency.BTC, currency.USDT) - tempDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Problem creating temp dir at %s: %s\n", tempDir, err) - } - defer func(path string) { - err = os.RemoveAll(path) - if err != nil { - t.Error(err) - } - }(tempDir) d := Data{ Config: &config.Config{}, - OutputPath: filepath.Join("..", "results"), + OutputPath: t.TempDir(), TemplatePath: "tpl.gohtml", OriginalCandles: []*gctkline.Item{ { @@ -319,9 +306,8 @@ func TestGenerateReport(t *testing.T) { }, }, } - d.OutputPath = tempDir d.Config.StrategySettings.DisableUSDTracking = true - err = d.GenerateReport() + err := d.GenerateReport() if err != nil { t.Error(err) } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 610485373e4..ad6b3390bd7 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1298,7 +1298,7 @@ {{else}} Biggest Drawdown - Start: {{ $val.MaxDrawdown.Highest.Time }} End: {{ $val.MaxDrawdown.Lowest.Time }} Drop: $ {{ $.Prettify.Decimal8 $val.MaxDrawdown.DrawdownPercent}}% + Start: {{ $val.MaxDrawdown.Highest.Time }} End: {{ $val.MaxDrawdown.Lowest.Time }} Drop: {{ $.Prettify.Decimal8 $val.MaxDrawdown.DrawdownPercent}}% {{ end }} diff --git a/currency/manager.go b/currency/manager.go index 246aaea0862..2a9799ff6eb 100644 --- a/currency/manager.go +++ b/currency/manager.go @@ -45,7 +45,7 @@ func (p *PairsManager) Get(a asset.Item) (*PairStore, error) { c, ok := p.Pairs[a] if !ok { return nil, - fmt.Errorf("cannot get pair store, asset type %s not supported", a) + fmt.Errorf("cannot get pair store, %v %w", a, asset.ErrNotSupported) } return c, nil } diff --git a/log/sublogger.go b/log/sublogger.go index 04aa587df37..0ffbc34b8a7 100644 --- a/log/sublogger.go +++ b/log/sublogger.go @@ -1,6 +1,7 @@ package log import ( + "fmt" "io" "strings" ) @@ -14,7 +15,7 @@ func NewSubLogger(name string) (*SubLogger, error) { RWM.RLock() if _, ok := SubLoggers[name]; ok { RWM.RUnlock() - return nil, ErrSubLoggerAlreadyRegistered + return nil, fmt.Errorf("'%v' %w", name, ErrSubLoggerAlreadyRegistered) } RWM.RUnlock() return registerNewSubLogger(name), nil From d06fdafbde5f6c95b9e853689c34f5015bd588f2 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 28 Apr 2022 13:51:52 +1000 Subject: [PATCH 131/171] Fixes merge problems --- backtester/config/configbuilder/main.go | 49 +++++++++---------- backtester/engine/setup.go | 4 +- .../eventhandlers/portfolio/portfolio_test.go | 5 +- backtester/eventhandlers/portfolio/setup.go | 2 +- .../trackingcurrencies_test.go | 11 +++-- exchanges/exchange.go | 4 +- exchanges/order/futures_test.go | 2 +- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 4dd1491cef2..275c5d30cc2 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -19,6 +19,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies" gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/file" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/database" dbPSQL "github.com/thrasher-corp/gocryptotrader/database/drivers/postgres" dbsqlite3 "github.com/thrasher-corp/gocryptotrader/database/drivers/sqlite3" @@ -290,21 +291,21 @@ func parseStrategySettings(cfg *config.Config, reader *bufio.Reader) error { if intNum > len(supported) || intNum <= 0 { return errors.New("unknown option") } - fund.Asset = supported[intNum-1].String() + fund.Asset = supported[intNum-1] } else { for i := range supported { if strings.EqualFold(response, supported[i].String()) { - fund.Asset = supported[i].String() + fund.Asset = supported[i] break } } - if fund.Asset == "" { + if fund.Asset == asset.Empty { return errors.New("unrecognised data option") } } fmt.Println("What is the individual currency to add funding to? eg BTC") - fund.Currency = quickParse(reader) + fund.Currency = currency.NewCode(quickParse(reader)) fmt.Printf("How much funding for %v?\n", fund.Currency) fund.InitialFunds, err = decimal.NewFromString(quickParse(reader)) if err != nil { @@ -502,7 +503,7 @@ func parseDataChoice(reader *bufio.Reader, multiCurrency bool) (string, error) { return "", errors.New("unrecognised data option") } -func parseKlineInterval(reader *bufio.Reader) (time.Duration, error) { +func parseKlineInterval(reader *bufio.Reader) (gctkline.Interval, error) { allCandles := gctkline.SupportedIntervals for i := range allCandles { fmt.Printf("%v. %s\n", i+1, allCandles[i].Word()) @@ -514,11 +515,11 @@ func parseKlineInterval(reader *bufio.Reader) (time.Duration, error) { if intNum > len(allCandles) || intNum <= 0 { return 0, errors.New("unknown option") } - return allCandles[intNum-1].Duration(), nil + return allCandles[intNum-1], nil } for i := range allCandles { if strings.EqualFold(response, allCandles[i].Word()) { - return allCandles[i].Duration(), nil + return allCandles[i], nil } } return 0, errors.New("unrecognised interval") @@ -575,19 +576,18 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* if intNum > len(supported) || intNum <= 0 { return nil, errors.New("unknown option") } - setting.Asset = supported[intNum-1].String() + setting.Asset = supported[intNum-1] } for i := range supported { if strings.EqualFold(response, supported[i].String()) { - setting.Asset = supported[i].String() + setting.Asset = supported[i] } } var f float64 fmt.Println("Enter the currency base. eg BTC") - setting.Base = quickParse(reader) - switch setting.Asset { - case asset.Spot.String(): + setting.Base = currency.NewCode(quickParse(reader)) + if setting.Asset == asset.Spot { setting.SpotDetails = &config.SpotDetails{} if !usingExchangeLevelFunding { fmt.Println("Enter the initial base funds. eg 0") @@ -601,27 +601,22 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* setting.SpotDetails.InitialBaseFunds = &iqf } } - case asset.Futures.String(): } fmt.Println("Enter the currency quote. eg USDT") - setting.Quote = quickParse(reader) + setting.Quote = currency.NewCode(quickParse(reader)) - switch setting.Asset { - case asset.Spot.String(): - if !usingExchangeLevelFunding { - fmt.Println("Enter the initial quote funds. eg 10000") - parseNum := quickParse(reader) - if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) - if err != nil { - return nil, err - } - iqf := decimal.NewFromFloat(f) - setting.SpotDetails.InitialQuoteFunds = &iqf + if setting.Asset == asset.Spot && !usingExchangeLevelFunding { + fmt.Println("Enter the initial quote funds. eg 10000") + parseNum := quickParse(reader) + if parseNum != "" { + f, err = strconv.ParseFloat(parseNum, 64) + if err != nil { + return nil, err } + iqf := decimal.NewFromFloat(f) + setting.SpotDetails.InitialQuoteFunds = &iqf } - case asset.Futures.String(): } fmt.Println("Do you want to set custom fees? If no, Backtester will use default fees for exchange y/n") diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index 5c9bda5bec2..ef4e7c8259d 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -559,7 +559,7 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange func (bt *BackTest) loadExchangePairAssetBase(exch string, base, quote currency.Code, ai asset.Item) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) { e, err := bt.exchangeManager.GetExchangeByName(exch) if err != nil { - return nil, currency.EMPTYPAIR, "", err + return nil, currency.EMPTYPAIR, asset.Empty, err } var cp, fPair currency.Pair @@ -572,7 +572,7 @@ func (bt *BackTest) loadExchangePairAssetBase(exch string, base, quote currency. fPair, err = exchangeBase.FormatExchangeCurrency(cp, ai) if err != nil { - return nil, currency.EMPTYPAIR, "", err + return nil, currency.EMPTYPAIR, asset.Empty, err } return e, fPair, ai, nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 76e00bca496..7701f0039ca 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -19,6 +19,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" @@ -1323,7 +1324,7 @@ func TestCreateLiquidationOrdersForExchange(t *testing.T) { } funds := &funding.FundManager{} - expectedError = nil + expectedError = config.ErrExchangeNotFound _, err = p.CreateLiquidationOrdersForExchange(ev, funds) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) @@ -1332,6 +1333,7 @@ func TestCreateLiquidationOrdersForExchange(t *testing.T) { ff := &ftx.FTX{} ff.Name = testExchange cp := currency.NewPair(currency.BTC, currency.USD) + expectedError = nil err = p.SetupCurrencySettingsMap(&exchange.Settings{Exchange: ff, Asset: asset.Futures, Pair: cp}) if err != nil { t.Error(err) @@ -1340,6 +1342,7 @@ func TestCreateLiquidationOrdersForExchange(t *testing.T) { if err != nil { t.Error(err) } + ev.Exchange = testExchange _, err = p.CreateLiquidationOrdersForExchange(ev, funds) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) diff --git a/backtester/eventhandlers/portfolio/setup.go b/backtester/eventhandlers/portfolio/setup.go index 6d13046680d..b22baad4255 100644 --- a/backtester/eventhandlers/portfolio/setup.go +++ b/backtester/eventhandlers/portfolio/setup.go @@ -44,7 +44,7 @@ func (p *Portfolio) SetupCurrencySettingsMap(setup *exchange.Settings) error { if setup.Exchange == nil { return errExchangeUnset } - if setup.Asset == "" { + if setup.Asset == asset.Empty { return errAssetUnset } if setup.Pair.IsEmpty() { diff --git a/backtester/funding/trackingcurrencies/trackingcurrencies_test.go b/backtester/funding/trackingcurrencies/trackingcurrencies_test.go index 72447f390e7..aeaad61d39d 100644 --- a/backtester/funding/trackingcurrencies/trackingcurrencies_test.go +++ b/backtester/funding/trackingcurrencies/trackingcurrencies_test.go @@ -6,13 +6,14 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/engine" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) var ( exch = "binance" - a = "spot" - b = "BTC" - q = "USDT" + a = asset.Spot + b = currency.BTC + q = currency.USDT ) func TestCreateUSDTrackingPairs(t *testing.T) { @@ -56,8 +57,8 @@ func TestCreateUSDTrackingPairs(t *testing.T) { if len(resp) != 1 { t.Error("expected 1 currency setting as it contains a USD equiv") } - s1.Base = "LTC" - s1.Quote = "BTC" + s1.Base = currency.LTC + s1.Quote = currency.BTC resp, err = CreateUSDTrackingPairs([]TrackingPair{s1}, em) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 95e6ed62fbf..e7aa91cc7cb 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1303,12 +1303,12 @@ func (b *Base) GetFuturesPositions(context.Context, asset.Item, currency.Pair, t // GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair func (b *Base) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, asset.Item, error) { - return currency.Code{}, "", common.ErrNotYetImplemented + return currency.Code{}, asset.Empty, common.ErrNotYetImplemented } // GetCurrencyForRealisedPNL returns where to put realised PNL // example 1: FTX PNL is paid out in USD to your spot wallet // example 2: Binance coin margined futures pays returns using the same currency eg BTC func (b *Base) GetCurrencyForRealisedPNL(_ asset.Item, _ currency.Pair) (currency.Code, asset.Item, error) { - return currency.Code{}, "", common.ErrNotYetImplemented + return currency.Code{}, asset.Empty, common.ErrNotYetImplemented } diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 89f3a4dcb46..c1975bf7f48 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -31,7 +31,7 @@ func (f *FakePNL) CalculatePNL(context.Context, *PNLCalculatorRequest) (*PNLResu // GetCurrencyForRealisedPNL overrides default pnl calculations func (f *FakePNL) GetCurrencyForRealisedPNL(realisedAsset asset.Item, realisedPair currency.Pair) (currency.Code, asset.Item, error) { if f.err != nil { - return realisedPair.Base, "", f.err + return realisedPair.Base, asset.Empty, f.err } return realisedPair.Base, realisedAsset, nil } From 73405ac70242959fb188e3c0e985716e9fd4de99 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 28 Apr 2022 14:07:52 +1000 Subject: [PATCH 132/171] The linter splintered across the glinting plinths --- backtester/config/config_test.go | 1 - backtester/data/data_test.go | 4 +--- backtester/engine/live.go | 2 +- backtester/engine/setup.go | 6 ++---- backtester/eventhandlers/statistics/statistics_test.go | 2 +- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 3a6a14e9442..da94cac7a8f 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -199,7 +199,6 @@ func TestValidateCurrencySettings(t *testing.T) { if !errors.Is(err, errBadInitialFunds) { t.Errorf("received: %v, expected: %v", err, errBadInitialFunds) } - } func TestValidateMinMaxes(t *testing.T) { diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index ff22008665c..d342c0ad128 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -24,8 +24,7 @@ func TestLatest(t *testing.T) { t.Parallel() var d Base d.AppendStream(&fakeDataHandler{time: 1}) - latest := d.Latest() - if latest != d.stream[d.offset] { + if latest := d.Latest(); latest != d.stream[d.offset] { t.Error("expected latest to match offset") } } @@ -80,7 +79,6 @@ func TestBaseDataFunctions(t *testing.T) { d.Reset() d.GetStream() d.SortStream() - } func TestSetup(t *testing.T) { diff --git a/backtester/engine/live.go b/backtester/engine/live.go index 9cc3232884d..e5257febe9d 100644 --- a/backtester/engine/live.go +++ b/backtester/engine/live.go @@ -73,7 +73,7 @@ func (bt *BackTest) loadLiveDataLoop(resp *kline.DataFromKline, cfg *config.Conf dates, err := gctkline.CalculateCandleDateRanges( startDate, startDate.AddDate(1, 0, 0), - gctkline.Interval(cfg.DataSettings.Interval), + cfg.DataSettings.Interval, 0) if err != nil { log.Errorf(common.Backtester, "%v. Please check your GoCryptoTrader configuration", err) diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index ef4e7c8259d..e0111d6def1 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -98,8 +98,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if cfg.FundingSettings.UseExchangeLevelFunding { for i := range cfg.FundingSettings.ExchangeLevelFunding { - var a asset.Item - a = cfg.FundingSettings.ExchangeLevelFunding[i].Asset + a := cfg.FundingSettings.ExchangeLevelFunding[i].Asset cq := cfg.FundingSettings.ExchangeLevelFunding[i].Currency var item *funding.Item item, err = funding.CreateItem(cfg.FundingSettings.ExchangeLevelFunding[i].ExchangeName, @@ -170,8 +169,7 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] == nil { portfolioRisk.CurrencySettings[cfg.CurrencySettings[i].ExchangeName] = make(map[asset.Item]map[currency.Pair]*risk.CurrencySettings) } - var a asset.Item - a = cfg.CurrencySettings[i].Asset + a := cfg.CurrencySettings[i].Asset if err != nil { return nil, fmt.Errorf( "%w for %v %v %v-%v. Err %v", diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 60b9f2f38ea..d6334897a03 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -917,7 +917,7 @@ func TestCalculateBiggestEventDrawdown(t *testing.T) { Low: decimal.NewFromInt(1339), }, } - resp, err = CalculateBiggestEventDrawdown(bogusEvent) + _, err = CalculateBiggestEventDrawdown(bogusEvent) if !errors.Is(err, gctcommon.ErrDateUnset) { t.Errorf("received %v expected %v", err, gctcommon.ErrDateUnset) } From f1f441af35dcead3d87e00438999b47bd055c152 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 29 Apr 2022 16:26:45 +1000 Subject: [PATCH 133/171] Many nits addressed. Now sells spot position on final candle --- README.md | 4 +- backtester/common/common.go | 18 ++++ backtester/common/common_test.go | 7 ++ backtester/common/common_types.go | 1 + backtester/config/config_test.go | 14 +-- backtester/config/configbuilder/main.go | 24 ++--- .../config/examples/ftx-cash-carry.strat | 8 +- backtester/data/kline/kline.go | 6 +- .../eventhandlers/exchange/exchange_test.go | 20 +--- .../portfolio/holdings/holdings.go | 2 - .../portfolio/holdings/holdings_test.go | 6 -- .../eventhandlers/portfolio/portfolio.go | 1 - .../eventhandlers/portfolio/portfolio_test.go | 5 +- .../eventhandlers/portfolio/risk/risk_test.go | 3 +- .../eventhandlers/portfolio/size/size_test.go | 4 - .../statistics/currencystatistics_test.go | 14 +-- .../statistics/fundingstatistics_test.go | 4 +- .../strategies/ftxcashandcarry/README.md | 2 +- .../ftxcashandcarry/ftxcashandcarry.go | 32 +++---- backtester/funding/collateralpair.go | 1 + backtester/funding/collateralpair_test.go | 12 +-- backtester/funding/funding.go | 91 +++++++++---------- backtester/funding/funding_types.go | 6 +- backtester/funding/item.go | 7 +- backtester/funding/item_test.go | 16 +++- backtester/funding/spotpair.go | 41 +++++---- backtester/main.go | 15 +-- backtester/report/chart_test.go | 3 +- ...ers_strategies_ftxcashandcarry_readme.tmpl | 2 +- engine/helpers.go | 2 +- exchanges/ftx/ftx_test.go | 1 - exchanges/order/futures.go | 23 ++--- 32 files changed, 170 insertions(+), 225 deletions(-) diff --git a/README.md b/README.md index 32305c68472..4b53290324e 100644 --- a/README.md +++ b/README.md @@ -144,10 +144,10 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| | [thrasher-](https://github.com/thrasher-) | 665 | -| [shazbert](https://github.com/shazbert) | 240 | +| [shazbert](https://github.com/shazbert) | 241 | | [gloriousCode](https://github.com/gloriousCode) | 195 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | -| [dependabot[bot]](https://github.com/apps/dependabot) | 68 | +| [dependabot[bot]](https://github.com/apps/dependabot) | 71 | | [xtda](https://github.com/xtda) | 47 | | [lrascao](https://github.com/lrascao) | 27 | | [Rots](https://github.com/Rots) | 15 | diff --git a/backtester/common/common.go b/backtester/common/common.go index 946448c7539..af94954a42d 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -105,3 +105,21 @@ func RegisterBacktesterSubLoggers() error { return nil } + +// PurgeColours removes colour information +func PurgeColours() { + ColourGreen = "" + ColourWhite = "" + ColourGrey = "" + ColourDefault = "" + ColourH1 = "" + ColourH2 = "" + ColourH3 = "" + ColourH4 = "" + ColourSuccess = "" + ColourInfo = "" + ColourDebug = "" + ColourWarn = "" + ColourDarkGrey = "" + ColourError = "" +} diff --git a/backtester/common/common_test.go b/backtester/common/common_test.go index f96319ebad1..60aca1b9f26 100644 --- a/backtester/common/common_test.go +++ b/backtester/common/common_test.go @@ -103,3 +103,10 @@ func TestFitStringToLimit(t *testing.T) { }) } } + +func TestPurgeColours(t *testing.T) { + PurgeColours() + if ColourSuccess != "" { + t.Error("expected purged colour") + } +} diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 0feef861202..2075d38043e 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -168,4 +168,5 @@ const ASCIILogo = ` / __ / __ / ___/ //_/ __/ _ \/ ___/ __/ _ \/ ___/ / /_/ / /_/ / /__/ ,< / /_/ __(__ ) /_/ __/ / /_____/\__,_/\___/_/|_|\__/\___/____/\__/\___/_/ + ` diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index da94cac7a8f..3d802ed7f75 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -449,18 +449,8 @@ func TestValidate(t *testing.T) { } func TestReadConfigFromFile(t *testing.T) { - tempDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Problem creating temp dir at %s: %s\n", tempDir, err) - } - defer func() { - err = os.RemoveAll(tempDir) - if err != nil { - t.Error(err) - } - }() - var passFile *os.File - passFile, err = ioutil.TempFile(tempDir, "*.start") + tempDir := t.TempDir() + passFile, err := ioutil.TempFile(tempDir, "*.start") if err != nil { t.Fatalf("Problem creating temp file at %v: %s\n", passFile, err) } diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 275c5d30cc2..683842f95fe 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -584,7 +584,7 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* } } - var f float64 + var d decimal.Decimal fmt.Println("Enter the currency base. eg BTC") setting.Base = currency.NewCode(quickParse(reader)) if setting.Asset == asset.Spot { @@ -593,12 +593,11 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the initial base funds. eg 0") parseNum := quickParse(reader) if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) + d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err } - iqf := decimal.NewFromFloat(f) - setting.SpotDetails.InitialBaseFunds = &iqf + setting.SpotDetails.InitialBaseFunds = &d } } } @@ -610,12 +609,11 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the initial quote funds. eg 10000") parseNum := quickParse(reader) if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) + d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err } - iqf := decimal.NewFromFloat(f) - setting.SpotDetails.InitialQuoteFunds = &iqf + setting.SpotDetails.InitialQuoteFunds = &d } } @@ -625,21 +623,19 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the maker-fee. eg 0.001") parseNum := quickParse(reader) if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) + d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err } - d := decimal.NewFromFloat(f) setting.MakerFee = &d } fmt.Println("Enter the taker-fee. eg 0.01") parseNum = quickParse(reader) if parseNum != "" { - f, err = strconv.ParseFloat(parseNum, 64) + d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err } - d := decimal.NewFromFloat(f) setting.TakerFee = &d } } @@ -680,18 +676,16 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("If the upper bound is 100, then the price can be unaffected. A minimum of 80 and a maximum of 100 means that the price will randomly be set between those bounds as a way of emulating slippage") fmt.Println("What is the lower bounds of slippage? eg 80") - f, err = strconv.ParseFloat(quickParse(reader), 64) + setting.MinimumSlippagePercent, err = decimal.NewFromString(quickParse(reader)) if err != nil { return nil, err } - setting.MinimumSlippagePercent = decimal.NewFromFloat(f) fmt.Println("What is the upper bounds of slippage? eg 100") - f, err = strconv.ParseFloat(quickParse(reader), 64) + setting.MaximumSlippagePercent, err = decimal.NewFromString(quickParse(reader)) if err != nil { return nil, err } - setting.MaximumSlippagePercent = decimal.NewFromFloat(f) } return &setting, nil diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 1dac4b22bfd..41394608b1e 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -36,10 +36,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "skip-candle-volume-fitting": false, + "skip-candle-volume-fitting": true, "use-exchange-order-limits": false, "use-exchange-pnl-calculation": false }, @@ -60,10 +58,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "skip-candle-volume-fitting": false, + "skip-candle-volume-fitting": true, "use-exchange-order-limits": false, "use-exchange-pnl-calculation": false } diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index 3225f859bec..0e8fac213de 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -47,7 +47,7 @@ func (d *DataFromKline) Load() error { ValidationIssues: d.Item.Candles[i].ValidationIssues, } klineData[i] = newKline - d.addedTimes[d.Item.Candles[i].Time.UTC().Unix()] = true + d.addedTimes[d.Item.Candles[i].Time.UTC().UnixNano()] = true } d.SetStream(klineData) @@ -63,9 +63,9 @@ func (d *DataFromKline) AppendResults(ki *gctkline.Item) { var gctCandles []gctkline.Candle for i := range ki.Candles { - if _, ok := d.addedTimes[ki.Candles[i].Time.Unix()]; !ok { + if _, ok := d.addedTimes[ki.Candles[i].Time.UnixNano()]; !ok { gctCandles = append(gctCandles, ki.Candles[i]) - d.addedTimes[ki.Candles[i].Time.Unix()] = true + d.addedTimes[ki.Candles[i].Time.UnixNano()] = true } } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index dd615854bad..36f0411059f 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -88,18 +88,10 @@ func TestSetCurrency(t *testing.T) { f := &ftx.FTX{} f.Name = testExchange cs := &Settings{ - Exchange: f, - UseRealOrders: true, - Pair: currency.NewPair(currency.BTC, currency.USDT), - Asset: asset.Spot, - ExchangeFee: decimal.Zero, - MakerFee: decimal.Zero, - TakerFee: decimal.Zero, - BuySide: MinMax{}, - SellSide: MinMax{}, - Leverage: Leverage{}, - MinimumSlippageRate: decimal.Zero, - MaximumSlippageRate: decimal.Zero, + Exchange: f, + UseRealOrders: true, + Pair: currency.NewPair(currency.BTC, currency.USDT), + Asset: asset.Spot, } e.SetExchangeAssetCurrencySettings(asset.Spot, currency.NewPair(currency.BTC, currency.USDT), cs) result, err := e.GetCurrencySettings(testExchange, asset.Spot, currency.NewPair(currency.BTC, currency.USDT)) @@ -360,14 +352,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { TakerFee: decimal.NewFromFloat(0.01), BuySide: MinMax{ MaximumSize: decimal.NewFromFloat(0.01), - MinimumSize: decimal.Zero, }, SellSide: MinMax{ MaximumSize: decimal.NewFromFloat(0.1), - MinimumSize: decimal.Zero, }, - Leverage: Leverage{}, - MinimumSlippageRate: decimal.Zero, MaximumSlippageRate: decimal.NewFromInt(1), Limits: limits, } diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index bf2243650c2..077a8db064f 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -30,8 +30,6 @@ func Create(ev ClosePriceReader, fundReader funding.IFundReader) (Holding, error Timestamp: ev.GetTime(), QuoteInitialFunds: funds.InitialFunds(), QuoteSize: funds.InitialFunds(), - BaseInitialFunds: decimal.Zero, - BaseSize: decimal.Zero, TotalInitialValue: funds.InitialFunds(), }, nil } else if ev.GetAssetType() == asset.Spot { diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index d438b8d1138..8ad3bc80ad2 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -147,8 +147,6 @@ func TestUpdateBuyStats(t *testing.T) { ClosePrice: decimal.NewFromInt(500), VolumeAdjustedPrice: decimal.NewFromInt(500), PurchasePrice: decimal.NewFromInt(500), - ExchangeFee: decimal.Zero, - Slippage: decimal.Zero, Order: &order.Detail{ Price: 500, Amount: 1, @@ -207,8 +205,6 @@ func TestUpdateBuyStats(t *testing.T) { ClosePrice: decimal.NewFromInt(500), VolumeAdjustedPrice: decimal.NewFromInt(500), PurchasePrice: decimal.NewFromInt(500), - ExchangeFee: decimal.Zero, - Slippage: decimal.Zero, Order: &order.Detail{ Price: 500, Amount: 0.5, @@ -338,8 +334,6 @@ func TestUpdateSellStats(t *testing.T) { ClosePrice: decimal.NewFromInt(500), VolumeAdjustedPrice: decimal.NewFromInt(500), PurchasePrice: decimal.NewFromInt(500), - ExchangeFee: decimal.Zero, - Slippage: decimal.Zero, Order: &order.Detail{ Price: 500, Amount: 1, diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index ca0b2e127fe..721bc763447 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -117,7 +117,6 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi if err != nil { return nil, err } - sizingFunds = collateralFunds.AvailableFunds() } } diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 7701f0039ca..85397c4a269 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -1383,7 +1383,10 @@ func TestCreateLiquidationOrdersForExchange(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - item.IncreaseAvailable(decimal.NewFromInt(1337)) + err = item.IncreaseAvailable(decimal.NewFromInt(1337)) + if !errors.Is(err, expectedError) { + t.Fatalf("received '%v' expected '%v'", err, expectedError) + } orders, err := p.CreateLiquidationOrdersForExchange(ev, funds) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) diff --git a/backtester/eventhandlers/portfolio/risk/risk_test.go b/backtester/eventhandlers/portfolio/risk/risk_test.go index 3538eb9d8a9..a345bdfa986 100644 --- a/backtester/eventhandlers/portfolio/risk/risk_test.go +++ b/backtester/eventhandlers/portfolio/risk/risk_test.go @@ -89,8 +89,7 @@ func TestEvaluateOrder(t *testing.T) { } h = append(h, holdings.Holding{ - Pair: currency.NewPair(currency.DOGE, currency.USDT), - BaseSize: decimal.Zero, + Pair: currency.NewPair(currency.DOGE, currency.USDT), }) o.Leverage = decimal.NewFromFloat(1.1) r.CurrencySettings[e][a][p].MaximumHoldingRatio = decimal.Zero diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index d0bd7a49c6e..943c5e14c93 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -14,7 +14,6 @@ import ( func TestSizingAccuracy(t *testing.T) { t.Parallel() globalMinMax := exchange.MinMax{ - MinimumSize: decimal.Zero, MaximumSize: decimal.NewFromInt(1), MaximumTotal: decimal.NewFromInt(10), } @@ -39,7 +38,6 @@ func TestSizingAccuracy(t *testing.T) { func TestSizingOverMaxSize(t *testing.T) { t.Parallel() globalMinMax := exchange.MinMax{ - MinimumSize: decimal.Zero, MaximumSize: decimal.NewFromFloat(0.5), MaximumTotal: decimal.NewFromInt(1337), } @@ -85,7 +83,6 @@ func TestMaximumBuySizeEqualZero(t *testing.T) { t.Parallel() globalMinMax := exchange.MinMax{ MinimumSize: decimal.NewFromInt(1), - MaximumSize: decimal.Zero, MaximumTotal: decimal.NewFromInt(1437), } sizer := Size{ @@ -105,7 +102,6 @@ func TestMaximumSellSizeEqualZero(t *testing.T) { t.Parallel() globalMinMax := exchange.MinMax{ MinimumSize: decimal.NewFromInt(1), - MaximumSize: decimal.Zero, MaximumTotal: decimal.NewFromInt(1437), } sizer := Size{ diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 6e605df5cea..e5b756a6580 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -138,12 +138,7 @@ func TestCalculateResults(t *testing.T) { } cs.Events = append(cs.Events, ev, ev3) cs.Events[0].DataEvent = &kline.Kline{ - Base: even2, - Open: decimal.Zero, - Close: decimal.Zero, - Low: decimal.Zero, - High: decimal.Zero, - Volume: decimal.Zero, + Base: even2, } err = cs.CalculateResults(decimal.NewFromFloat(0.03)) if err != nil { @@ -151,12 +146,7 @@ func TestCalculateResults(t *testing.T) { } cs.Events[1].DataEvent = &kline.Kline{ - Base: even2, - Open: decimal.Zero, - Close: decimal.Zero, - Low: decimal.Zero, - High: decimal.Zero, - Volume: decimal.Zero, + Base: even2, } err = cs.CalculateResults(decimal.NewFromFloat(0.03)) if err != nil { diff --git a/backtester/eventhandlers/statistics/fundingstatistics_test.go b/backtester/eventhandlers/statistics/fundingstatistics_test.go index 3f5e47ef7e8..3e0ea5dae90 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics_test.go +++ b/backtester/eventhandlers/statistics/fundingstatistics_test.go @@ -134,9 +134,7 @@ func TestCalculateIndividualFundingStatistics(t *testing.T) { { USDValue: decimal.NewFromInt(1337), }, - { - USDValue: decimal.Zero, - }, + {}, }, } rs := []relatedCurrencyPairStatistics{ diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/README.md b/backtester/eventhandlers/strategies/ftxcashandcarry/README.md index 9acae0e4645..366ffd9e962 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/README.md +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/README.md @@ -31,7 +31,7 @@ On the last event, the strategy will close the SHORT position by raising a LONG - This strategy *requires* `Exchange Level Funding` aka [use-exchange-level-funding](/backtester/config/README.md). ### Creating a strategy config --The long-dated futures contract will need to be part of the `currency-settings` of the contract +- The long-dated futures contract will need to be part of the `currency-settings` of the contract - Funding for purchasing SPOT assets will need to be part of `funding-settings` - See the [example config](./config/examples/ftx-cash-carry.strat) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 7284ae607b1..b4fddea580f 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -22,8 +22,7 @@ func (s *Strategy) Name() string { return Name } -// Description provides a nice overview of the strategy -// be it definition of terms or to highlight its purpose +// Description describes the strategy func (s *Strategy) Description() string { return description } @@ -35,9 +34,7 @@ func (s *Strategy) OnSignal(data.Handler, funding.IFundingTransferer, portfolio. return nil, base.ErrSimultaneousProcessingOnly } -// SupportsSimultaneousProcessing highlights whether the strategy can handle multiple currency calculation -// There is nothing actually stopping this strategy from considering multiple currencies at once -// but for demonstration purposes, this strategy does not +// SupportsSimultaneousProcessing this strategy only supports simultaneous signal processing func (s *Strategy) SupportsSimultaneousProcessing() bool { return true } @@ -105,6 +102,8 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra return response, nil } +// createSignals creates signals based on the relationships between +// futures and spot signals func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresSignal *signal.Signal, diffBetweenFuturesSpot decimal.Decimal, isLastEvent bool) ([]signal.Event, error) { if spotSignal == nil { return nil, fmt.Errorf("%w missing spot signal", common.ErrNilArguments) @@ -133,19 +132,18 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS spotSignal.FillDependentEvent = futuresSignal // only appending spotSignal as futuresSignal will be raised later response = append(response, spotSignal) - case len(pos) > 0 && - pos[len(pos)-1].Status == order.Open && + case pos[len(pos)-1].Status == order.Open && isLastEvent: futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("Closing position on last event") + spotSignal.SetDirection(order.Sell) response = append(response, spotSignal, futuresSignal) - case len(pos) > 0 && pos[len(pos)-1].Status == order.Open && + case pos[len(pos)-1].Status == order.Open && diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReasonf("Closing position. Threshold %v", s.closeShortDistancePercentage) response = append(response, spotSignal, futuresSignal) - case len(pos) > 0 && - pos[len(pos)-1].Status == order.Closed && + case pos[len(pos)-1].Status == order.Closed && diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): futuresSignal.SetDirection(order.Short) futuresSignal.SetPrice(futuresSignal.ClosePrice) @@ -157,6 +155,8 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS return response, nil } +// sortSignals links spot and futures signals in order to create cash +// and carry signals func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { if len(d) == 0 { return nil, errNoSignals @@ -185,30 +185,30 @@ func sortSignals(d []data.Handler) (map[currency.Pair]cashCarrySignals, error) { // validate that each set of signals is matched for _, v := range response { if v.futureSignal == nil { - return nil, errNotSetup + return nil, fmt.Errorf("%w missing future signal", errNotSetup) } if v.spotSignal == nil { - return nil, errNotSetup + return nil, fmt.Errorf("%w missing spot signal", errNotSetup) } } return response, nil } -// SetCustomSettings not required for DCA +// SetCustomSettings can override default settings func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) error { for k, v := range customSettings { switch k { case openShortDistancePercentageString: osdp, ok := v.(float64) if !ok || osdp <= 0 { - return fmt.Errorf("%w provided rsi-high value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + return fmt.Errorf("%w provided openShortDistancePercentage value could not be parsed: %v", base.ErrInvalidCustomSettings, v) } s.openShortDistancePercentage = decimal.NewFromFloat(osdp) case closeShortDistancePercentageString: csdp, ok := v.(float64) if !ok || csdp <= 0 { - return fmt.Errorf("%w provided rsi-low value could not be parsed: %v", base.ErrInvalidCustomSettings, v) + return fmt.Errorf("%w provided closeShortDistancePercentage value could not be parsed: %v", base.ErrInvalidCustomSettings, v) } s.closeShortDistancePercentage = decimal.NewFromFloat(csdp) default: @@ -219,7 +219,7 @@ func (s *Strategy) SetCustomSettings(customSettings map[string]interface{}) erro return nil } -// SetDefaults not required for DCA +// SetDefaults sets default values for overridable custom settings func (s *Strategy) SetDefaults() { s.openShortDistancePercentage = decimal.Zero s.closeShortDistancePercentage = decimal.Zero diff --git a/backtester/funding/collateralpair.go b/backtester/funding/collateralpair.go index 1a9d31c3370..b747603d54f 100644 --- a/backtester/funding/collateralpair.go +++ b/backtester/funding/collateralpair.go @@ -99,6 +99,7 @@ func (c *CollateralPair) Reserve(amount decimal.Decimal, side gctorder.Side) err } // Liquidate kills your funds and future +// all value storage are reduced to zero when triggered func (c *CollateralPair) Liquidate() { c.collateral.available = decimal.Zero c.collateral.reserved = decimal.Zero diff --git a/backtester/funding/collateralpair_test.go b/backtester/funding/collateralpair_test.go index 00e7978d821..b8c5f852fba 100644 --- a/backtester/funding/collateralpair_test.go +++ b/backtester/funding/collateralpair_test.go @@ -114,9 +114,7 @@ func TestCollateralUpdateContracts(t *testing.T) { asset: asset.Futures, isCollateral: true, }, - contract: &Item{asset: asset.Futures, - available: decimal.Zero, - }, + contract: &Item{asset: asset.Futures}, currentDirection: &b, } leet := decimal.NewFromInt(1337) @@ -154,9 +152,7 @@ func TestCollateralReleaseContracts(t *testing.T) { asset: asset.Futures, isCollateral: true, }, - contract: &Item{asset: asset.Futures, - available: decimal.Zero, - }, + contract: &Item{asset: asset.Futures}, currentDirection: &b, } @@ -241,9 +237,7 @@ func TestCollateralReserve(t *testing.T) { isCollateral: true, available: decimal.NewFromInt(1337), }, - contract: &Item{asset: asset.Futures, - available: decimal.Zero, - }, + contract: &Item{asset: asset.Futures}, } var expectedError error err := c.Reserve(decimal.NewFromInt(1), gctorder.Long) diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 2dbbf7ed9c8..6f7c1191d1a 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -117,8 +117,8 @@ func (f *FundManager) CreateSnapshot(t time.Time) { if f.items[i].isCollateral { f.items[i].initialFunds = f.items[i].available } - } else if _, ok := f.items[i].snapshot[t.Unix()]; ok { - f.items[i].snapshot[t.Unix()] = ItemSnapshot{} + } else if _, ok := f.items[i].snapshot[t.UnixNano()]; ok { + f.items[i].snapshot[t.UnixNano()] = ItemSnapshot{} } iss := ItemSnapshot{ @@ -142,7 +142,7 @@ func (f *FundManager) CreateSnapshot(t time.Time) { iss.USDValue = usdClosePrice.Mul(f.items[i].available) } - f.items[i].snapshot[t.Unix()] = iss + f.items[i].snapshot[t.UnixNano()] = iss } } @@ -164,31 +164,10 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { } if f.items[i].asset.IsFutures() && k.Item.Asset.IsFutures() { if f.items[i].isCollateral { - usdCandles := gctkline.Item{ - Exchange: k.Item.Exchange, - Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, - Asset: k.Item.Asset, - Interval: k.Item.Interval, - Candles: make([]gctkline.Candle, len(k.Item.Candles)), - } - copy(usdCandles.Candles, k.Item.Candles) - for j := range usdCandles.Candles { - // usd stablecoins do not always match in value, - // this is a simplified implementation that can allow - // USD tracking for many currencies across many exchanges - // without retrieving n candle history and exchange rates - usdCandles.Candles[j].Open = 1 - usdCandles.Candles[j].High = 1 - usdCandles.Candles[j].Low = 1 - usdCandles.Candles[j].Close = 1 - } - cpy := *k - cpy.Item = usdCandles - if err := cpy.Load(); err != nil { + err := f.setUSDCandles(k, i) + if err != nil { return err } - f.items[i].trackingCandles = &cpy - quoteSet = true } else { f.items[i].trackingCandles = k baseSet = true @@ -213,30 +192,10 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { continue } if f.items[i].trackingCandles == nil { - usdCandles := gctkline.Item{ - Exchange: k.Item.Exchange, - Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, - Asset: k.Item.Asset, - Interval: k.Item.Interval, - Candles: make([]gctkline.Candle, len(k.Item.Candles)), - } - copy(usdCandles.Candles, k.Item.Candles) - for j := range usdCandles.Candles { - // usd stablecoins do not always match in value, - // this is a simplified implementation that can allow - // USD tracking for many currencies across many exchanges - // without retrieving n candle history and exchange rates - usdCandles.Candles[j].Open = 1 - usdCandles.Candles[j].High = 1 - usdCandles.Candles[j].Low = 1 - usdCandles.Candles[j].Close = 1 - } - cpy := *k - cpy.Item = usdCandles - if err := cpy.Load(); err != nil { + err := f.setUSDCandles(k, i) + if err != nil { return err } - f.items[i].trackingCandles = &cpy } quoteSet = true } @@ -248,6 +207,36 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error { return fmt.Errorf("%w %v %v %v", errCannotMatchTrackingToItem, k.Item.Exchange, k.Item.Asset, k.Item.Pair) } +// setUSDCandles sets usd tracking candles +// usd stablecoins do not always match in value, +// this is a simplified implementation that can allow +// USD tracking for many currencies across many exchanges +func (f *FundManager) setUSDCandles(k *kline.DataFromKline, i int) error { + usdCandles := gctkline.Item{ + Exchange: k.Item.Exchange, + Pair: currency.Pair{Delimiter: k.Item.Pair.Delimiter, Base: f.items[i].currency, Quote: currency.USD}, + Asset: k.Item.Asset, + Interval: k.Item.Interval, + Candles: make([]gctkline.Candle, len(k.Item.Candles)), + } + for j := range usdCandles.Candles { + usdCandles.Candles[j] = gctkline.Candle{ + Time: k.Item.Candles[j].Time, + Open: 1, + High: 1, + Low: 1, + Close: 1, + } + } + cpy := *k + cpy.Item = usdCandles + if err := cpy.Load(); err != nil { + return err + } + f.items[i].trackingCandles = &cpy + return nil +} + // CreatePair adds two funding items and associates them with one another // the association allows for the same currency to be used multiple times when // usingExchangeLevelFunding is false. eg BTC-USDT and LTC-USDT do not share the same @@ -419,7 +408,10 @@ func (f *FundManager) Transfer(amount decimal.Decimal, sender, receiver *Item, i if err != nil { return err } - receiver.IncreaseAvailable(receiveAmount) + err = receiver.IncreaseAvailable(receiveAmount) + if err != nil { + return err + } return sender.Release(sendAmount, decimal.Zero) } @@ -654,7 +646,6 @@ func (f *FundManager) HasExchangeBeenLiquidated(ev common.EventHandler) bool { if ev.GetExchange() == f.items[i].exchange { return f.items[i].isLiquidated } - continue } return false } diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 86c6d838bcf..0bc9fb9e397 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -55,7 +55,7 @@ type IFundingPair interface { FundReleaser() IFundReleaser } -// IFundReader allows a connoisseur to read +// IFundReader can read // either collateral or pair details type IFundReader interface { GetPairReader() (IPairReader, error) @@ -69,7 +69,7 @@ type IFundReserver interface { Reserve(decimal.Decimal, order.Side) error } -// IFundReleaser allows a connoisseur to read +// IFundReleaser can read // or release pair or collateral funds type IFundReleaser interface { IFundReader @@ -99,7 +99,7 @@ type ICollateralReader interface { // IPairReleaser limits funding usage for exchange event handling type IPairReleaser interface { IPairReader - IncreaseAvailable(decimal.Decimal, order.Side) + IncreaseAvailable(decimal.Decimal, order.Side) error Release(decimal.Decimal, decimal.Decimal, order.Side) error Liquidate() } diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 8bf8d50fcf3..23617fbf833 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -52,11 +52,12 @@ func (i *Item) Release(amount, diff decimal.Decimal) error { } // IncreaseAvailable adds funding to the available amount -func (i *Item) IncreaseAvailable(amount decimal.Decimal) { +func (i *Item) IncreaseAvailable(amount decimal.Decimal) error { if amount.IsNegative() || amount.IsZero() { - return + return fmt.Errorf("%w amount <= zero", errZeroAmountReceived) } i.available = i.available.Add(amount) + return nil } // CanPlaceOrder checks if the item has any funds available @@ -115,7 +116,7 @@ func (i *Item) MatchesExchange(item *Item) bool { return i != nil && item != nil && i.exchange == item.exchange } -// TakeProfit increases available funds for a futures asset +// TakeProfit updates available funds for a futures collateral item func (i *Item) TakeProfit(amount decimal.Decimal) error { if i.asset.IsFutures() && !i.isCollateral { return fmt.Errorf("%v %v %v %w cannot add profit to contracts", i.exchange, i.asset, i.currency, ErrNotCollateral) diff --git a/backtester/funding/item_test.go b/backtester/funding/item_test.go index 5fa60479e2d..82d97158180 100644 --- a/backtester/funding/item_test.go +++ b/backtester/funding/item_test.go @@ -90,15 +90,21 @@ func TestReserve(t *testing.T) { func TestIncreaseAvailable(t *testing.T) { t.Parallel() i := Item{} - i.IncreaseAvailable(elite) - if !i.available.Equal(elite) { - t.Errorf("expected %v", elite) + err := i.IncreaseAvailable(elite) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) } - i.IncreaseAvailable(decimal.Zero) - i.IncreaseAvailable(neg) if !i.available.Equal(elite) { t.Errorf("expected %v", elite) } + err = i.IncreaseAvailable(decimal.Zero) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } + err = i.IncreaseAvailable(neg) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } } func TestRelease(t *testing.T) { diff --git a/backtester/funding/spotpair.go b/backtester/funding/spotpair.go index 343fe0aaac0..b15b55b002e 100644 --- a/backtester/funding/spotpair.go +++ b/backtester/funding/spotpair.go @@ -42,9 +42,9 @@ func (p *SpotPair) QuoteAvailable() decimal.Decimal { // changes which currency to affect based on the order side func (p *SpotPair) Reserve(amount decimal.Decimal, side order.Side) error { switch side { - case order.Buy: + case order.Buy, order.Bid: return p.quote.Reserve(amount) - case order.Sell: + case order.Sell, order.Ask: return p.base.Reserve(amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", @@ -61,29 +61,34 @@ func (p *SpotPair) Reserve(amount decimal.Decimal, side order.Side) error { // changes which currency to affect based on the order side func (p *SpotPair) Release(amount, diff decimal.Decimal, side order.Side) error { switch side { - case order.Buy: + case order.Buy, order.Bid: return p.quote.Release(amount, diff) - case order.Sell: + case order.Sell, order.Ask: return p.base.Release(amount, diff) - default: - return fmt.Errorf("%w for %v %v %v. Unknown side %v", - errCannotAllocate, - p.base.exchange, - p.base.asset, - p.base.currency, - side) } + return fmt.Errorf("%w for %v %v %v. Unknown side %v", + errCannotAllocate, + p.base.exchange, + p.base.asset, + p.base.currency, + side) } // IncreaseAvailable adds funding to the available amount // changes which currency to affect based on the order side -func (p *SpotPair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { +func (p *SpotPair) IncreaseAvailable(amount decimal.Decimal, side order.Side) error { switch side { - case order.Buy: - p.base.IncreaseAvailable(amount) - case order.Sell: - p.quote.IncreaseAvailable(amount) + case order.Buy, order.Bid: + return p.base.IncreaseAvailable(amount) + case order.Sell, order.Ask: + return p.quote.IncreaseAvailable(amount) } + return fmt.Errorf("%w for %v %v %v. Unknown side %v", + errCannotAllocate, + p.base.exchange, + p.base.asset, + p.base.currency, + side) } // CanPlaceOrder does a > 0 check to see if there are any funds @@ -91,9 +96,9 @@ func (p *SpotPair) IncreaseAvailable(amount decimal.Decimal, side order.Side) { // changes which currency to affect based on the order side func (p *SpotPair) CanPlaceOrder(side order.Side) bool { switch side { - case order.Buy: + case order.Buy, order.Bid: return p.quote.CanPlaceOrder() - case order.Sell: + case order.Sell, order.Ask: return p.base.CanPlaceOrder() } return false diff --git a/backtester/main.go b/backtester/main.go index fa0688e5833..982ebf3481f 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -79,20 +79,7 @@ func main() { flag.Parse() if !colourOutput { - common.ColourGreen = "" - common.ColourWhite = "" - common.ColourGrey = "" - common.ColourDefault = "" - common.ColourH1 = "" - common.ColourH2 = "" - common.ColourH3 = "" - common.ColourH4 = "" - common.ColourSuccess = "" - common.ColourInfo = "" - common.ColourDebug = "" - common.ColourWarn = "" - common.ColourDarkGrey = "" - common.ColourError = "" + common.PurgeColours() } var bt *backtest.BackTest var cfg *config.Config diff --git a/backtester/report/chart_test.go b/backtester/report/chart_test.go index a3636010567..53087ff881a 100644 --- a/backtester/report/chart_test.go +++ b/backtester/report/chart_test.go @@ -81,8 +81,7 @@ func TestCreateHoldingsOverTimeChart(t *testing.T) { Available: decimal.NewFromInt(1337), }, { - Time: tt, - Available: decimal.Zero, + Time: tt, }, }, }, diff --git a/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl b/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl index e0aa19b29a5..58940db86d0 100644 --- a/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl +++ b/cmd/documentation/backtester_templates/backtester_eventhandlers_strategies_ftxcashandcarry_readme.tmpl @@ -13,7 +13,7 @@ On the last event, the strategy will close the SHORT position by raising a LONG - This strategy *requires* `Exchange Level Funding` aka [use-exchange-level-funding](/backtester/config/README.md). ### Creating a strategy config --The long-dated futures contract will need to be part of the `currency-settings` of the contract +- The long-dated futures contract will need to be part of the `currency-settings` of the contract - Funding for purchasing SPOT assets will need to be part of `funding-settings` - See the [example config](./config/examples/ftx-cash-carry.strat) diff --git a/engine/helpers.go b/engine/helpers.go index 72f3512cf89..a6194634082 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -137,7 +137,7 @@ func (bot *Engine) SetSubsystem(subSystemName string, enable bool) error { bot.ExchangeManager, bot.CommunicationsManager, &bot.ServicesWG, - true, + bot.Settings.EnableFuturesTracking, bot.Settings.Verbose) if err != nil { return err diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 8697848cd22..a9aaae9ac3e 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1846,7 +1846,6 @@ func TestScaleCollateral(t *testing.T) { Asset: asset.Spot, Side: order.Buy, FreeCollateral: decimal.NewFromFloat(v[v2].Total), - USDPrice: decimal.Zero, IsLiquidating: true, CalculateOffline: true, }) diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index bcc028b85ad..13bcf7068ce 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -442,7 +442,6 @@ func (p *PositionTracker) TrackPNLByTime(t time.Time, currentPrice float64) erro result := &PNLResult{ Time: t, Price: price, - Fee: decimal.Zero, Status: p.status, } if p.currentDirection.IsLong() { @@ -497,17 +496,12 @@ func (p *PositionTracker) Liquidate(price decimal.Decimal, t time.Time) error { p.realisedPNL = decimal.Zero p.unrealisedPNL = decimal.Zero _, err = upsertPNLEntry(p.pnlHistory, &PNLResult{ - Time: t, - UnrealisedPNL: decimal.Zero, - RealisedPNLBeforeFees: decimal.Zero, - RealisedPNL: decimal.Zero, - Price: price, - Exposure: decimal.Zero, - Direction: SideNA, - Fee: decimal.Zero, - IsLiquidated: true, - IsOrder: true, - Status: p.status, + Time: t, + Price: price, + Direction: SideNA, + IsLiquidated: true, + IsOrder: true, + Status: p.status, }) return err @@ -728,10 +722,7 @@ func (p *PNLCalculator) CalculatePNL(_ context.Context, calc *PNLCalculatorReque var previousPNL *PNLResult if len(calc.PNLHistory) > 0 { for i := len(calc.PNLHistory) - 1; i >= 0; i-- { - if calc.PNLHistory[i].Time.Equal(calc.Time) { - continue - } - if !calc.PNLHistory[i].IsOrder { + if calc.PNLHistory[i].Time.Equal(calc.Time) || !calc.PNLHistory[i].IsOrder { continue } previousPNL = &calc.PNLHistory[i] From 41f72ad5f3ccc88ef5be834552db3c25fc866cc9 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 2 May 2022 15:08:15 +1000 Subject: [PATCH 134/171] Adds forgotten coverage --- backtester/funding/item.go | 2 +- backtester/report/chart.go | 34 ++++----- exchanges/order/futures.go | 2 +- exchanges/order/futures_test.go | 122 ++++++++++++++++++++++++++++++++ exchanges/order/order_types.go | 1 + 5 files changed, 137 insertions(+), 24 deletions(-) diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 23617fbf833..713b9b58116 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -116,7 +116,7 @@ func (i *Item) MatchesExchange(item *Item) bool { return i != nil && item != nil && i.exchange == item.exchange } -// TakeProfit updates available funds for a futures collateral item +// TakeProfit increases/decreases available funds for a futures collateral item func (i *Item) TakeProfit(amount decimal.Decimal) error { if i.asset.IsFutures() && !i.isCollateral { return fmt.Errorf("%v %v %v %w cannot add profit to contracts", i.exchange, i.asset, i.currency, ErrNotCollateral) diff --git a/backtester/report/chart.go b/backtester/report/chart.go index d1b7547b4de..8e381b80b5d 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -126,36 +126,26 @@ func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pai if items == nil { return nil, fmt.Errorf("%w missing currency pair statistics", common.ErrNilArguments) } - var currs []linkCurrencyDiff + currs := make(map[currency.Pair]linkCurrencyDiff) response := &Chart{} for _, assetMap := range items { for item, pairMap := range assetMap { - if !item.IsFutures() { - continue - } for pair, result := range pairMap { - currs = append(currs, linkCurrencyDiff{ - FuturesPair: pair, - SpotPair: result.UnderlyingPair, - FuturesEvents: result.Events, - }) - } - } - } - for _, assetMap := range items { - for item, pairMap := range assetMap { - if item.IsFutures() { - continue - } - for pair, result := range pairMap { - for i := range currs { - if pair.Equal(currs[i].SpotPair) { - currs[i].SpotEvents = result.Events - } + diff, ok := currs[pair] + if !ok { + diff = linkCurrencyDiff{} + } + if item.IsFutures() { + diff.FuturesPair = pair + diff.SpotPair = result.UnderlyingPair + diff.FuturesEvents = result.Events + } else { + diff.SpotEvents = result.Events } } } } + for i := range currs { if currs[i].FuturesEvents == nil || currs[i].SpotEvents == nil { continue diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 13bcf7068ce..fa553d99d9e 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -488,7 +488,7 @@ func (p *PositionTracker) Liquidate(price decimal.Decimal, t time.Time) error { return err } if !latest.Time.Equal(t) { - return fmt.Errorf("%w. Cannot liquidate from different time. Order closed on %v. Liquidation request on %v Status: %v", ErrPositionClosed, latest.Time, t, p.status) + return fmt.Errorf("%w cannot liquidate from a different time. PNL snapshot %v. Liquidation request on %v Status: %v", errCannotLiquidate, latest.Time, t, p.status) } p.status = Liquidated p.currentDirection = SideNA diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index c1975bf7f48..9fc35ee1b63 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -935,3 +935,125 @@ func TestMPTUpdateOpenPositionUnrealisedPNL(t *testing.T) { t.Fatalf("received '%v' expected '%v", err, expectedError) } } + +func TestMPTLiquidate(t *testing.T) { + t.Parallel() + item := asset.Futures + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Error(err) + } + e := &MultiPositionTracker{ + exchange: testExchange, + exchangePNLCalculation: &FakePNL{}, + asset: item, + orderPositions: make(map[string]*PositionTracker), + } + + err = e.Liquidate(decimal.Zero, time.Time{}) + if !errors.Is(err, ErrPositionsNotLoadedForPair) { + t.Error(err) + } + + setup := &PositionTrackerSetup{ + Pair: pair, + Asset: item, + } + _, err = e.SetupPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + + tt := time.Now() + err = e.TrackNewOrder(&Detail{ + Date: tt, + Exchange: testExchange, + Pair: pair, + AssetType: item, + Side: Long, + ID: "lol", + Price: 1, + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + + err = e.Liquidate(decimal.Zero, time.Time{}) + if !errors.Is(err, errCannotLiquidate) { + t.Error(err) + } + + err = e.Liquidate(decimal.Zero, tt) + if !errors.Is(err, nil) { + t.Error(err) + } + + if e.positions[0].status != Liquidated { + t.Errorf("recieved '%v' expected '%v'", e.positions[0].status, Liquidated) + } + if !e.positions[0].exposure.IsZero() { + t.Errorf("recieved '%v' expected '%v'", e.positions[0].exposure, 0) + } + + e = nil + err = e.Liquidate(decimal.Zero, tt) + if !errors.Is(err, common.ErrNilPointer) { + t.Error(err) + } +} + +func TestPositionLiquidate(t *testing.T) { + t.Parallel() + item := asset.Futures + pair, err := currency.NewPairFromStrings("BTC", "1231") + if !errors.Is(err, nil) { + t.Error(err) + } + p := &PositionTracker{ + contractPair: pair, + asset: item, + exchange: testExchange, + PNLCalculation: &PNLCalculator{}, + status: Open, + openingDirection: Long, + } + + tt := time.Now() + err = p.TrackNewOrder(&Detail{ + Date: tt, + Exchange: testExchange, + Pair: pair, + AssetType: item, + Side: Long, + ID: "lol", + Price: 1, + Amount: 1, + }) + if !errors.Is(err, nil) { + t.Error(err) + } + + err = p.Liquidate(decimal.Zero, time.Time{}) + if !errors.Is(err, errCannotLiquidate) { + t.Error(err) + } + + err = p.Liquidate(decimal.Zero, tt) + if !errors.Is(err, nil) { + t.Error(err) + } + + if p.status != Liquidated { + t.Errorf("recieved '%v' expected '%v'", p.status, Liquidated) + } + if !p.exposure.IsZero() { + t.Errorf("recieved '%v' expected '%v'", p.exposure, 0) + } + + p = nil + err = p.Liquidate(decimal.Zero, tt) + if !errors.Is(err, common.ErrNilPointer) { + t.Error(err) + } +} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 2d7bae0ba48..063b001a72c 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -21,6 +21,7 @@ var ( ErrAmountIsInvalid = errors.New("order amount is equal or less than zero") ErrPriceMustBeSetIfLimitOrder = errors.New("order price must be set if limit order type is desired") ErrOrderIDNotSet = errors.New("order id or client order id is not set") + errCannotLiquidate = errors.New("cannot liquidate position") ) // Submit contains all properties of an order that may be required From 4257cc0d61f6cc769a343ce0d087027529bfab0a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 3 May 2022 16:33:26 +1000 Subject: [PATCH 135/171] Adds ability to size futures contracts to match spot positions. --- backtester/engine/backtest.go | 4 + backtester/eventhandlers/exchange/exchange.go | 4 +- .../eventhandlers/portfolio/portfolio.go | 20 +++-- .../eventhandlers/portfolio/size/size.go | 75 +++++++++++++++---- .../eventhandlers/portfolio/size/size_test.go | 27 ++++++- .../ftxcashandcarry/ftxcashandcarry.go | 6 +- backtester/eventtypes/signal/signal.go | 6 ++ backtester/eventtypes/signal/signal_types.go | 5 ++ backtester/funding/spotpair.go | 3 +- 9 files changed, 121 insertions(+), 29 deletions(-) diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 352f8150ad1..237e3abb7ea 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -415,6 +415,10 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser) if err != nil { log.Errorf(common.Backtester, "SetEventForOffset %v %v %v %v", fde.GetExchange(), fde.GetAssetType(), fde.Pair(), err) } + od := ev.GetOrder() + if fde.MatchOrderAmount() && od != nil { + fde.SetAmount(ev.GetAmount()) + } fde.AppendReasonf("raising event after %v %v %v fill", ev.GetExchange(), ev.GetAssetType(), ev.Pair()) bt.EventQueue.AppendEvent(fde) } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index b01a5185213..319519924fb 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -318,13 +318,13 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c func reduceAmountToFitPortfolioLimit(adjustedPrice, amount, sizedPortfolioTotal decimal.Decimal, side gctorder.Side) decimal.Decimal { switch side { - case gctorder.Buy: + case gctorder.Buy, gctorder.Bid: if adjustedPrice.Mul(amount).GreaterThan(sizedPortfolioTotal) { // adjusted amounts exceeds portfolio manager's allowed funds // the amount has to be reduced to equal the sizedPortfolioTotal amount = sizedPortfolioTotal.Div(adjustedPrice) } - case gctorder.Sell: + case gctorder.Sell, gctorder.Ask: if amount.GreaterThan(sizedPortfolioTotal) { amount = sizedPortfolioTotal } diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 721bc763447..b95c409bc0a 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -40,9 +40,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi if funds == nil { return nil, funding.ErrFundsNotFound } - if ev.GetDirection() == common.ClosePosition { - log.Debugln(log.Currency, "") - } + o := &order.Order{ Base: event.Base{ Offset: ev.GetOffset(), @@ -87,6 +85,9 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi var sizingFunds decimal.Decimal var side = ev.GetDirection() if ev.GetAssetType() == asset.Spot { + if ev.GetDirection() == common.ClosePosition { + side = gctorder.Sell + } pReader, err := funds.GetPairReader() if err != nil { return nil, err @@ -156,14 +157,14 @@ func cannotPurchase(ev signal.Event, o *order.Order) (*order.Order, error) { return o, nil } -func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, sizedOrder *order.Order) (*order.Order, error) { +func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, ev *order.Order) (*order.Order, error) { var evaluatedOrder *order.Order cm, err := p.GetComplianceManager(originalOrderSignal.GetExchange(), originalOrderSignal.GetAssetType(), originalOrderSignal.Pair()) if err != nil { return nil, err } - evaluatedOrder, err = p.riskManager.EvaluateOrder(sizedOrder, p.GetLatestHoldingsForAllCurrencies(), cm.GetLatestSnapshot()) + evaluatedOrder, err = p.riskManager.EvaluateOrder(ev, p.GetLatestHoldingsForAllCurrencies(), cm.GetLatestSnapshot()) if err != nil { originalOrderSignal.AppendReason(err.Error()) switch d.GetDirection() { @@ -229,8 +230,13 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) case common.ClosePosition: - err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) - sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) + if originalOrderSignal.AssetType.IsFutures() { + err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) + } else { + err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + sizedOrder.AllocatedSize = sizedOrder.Amount + } default: err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice), gctorder.Buy) sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice) diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index b9c40c82d5d..31f65e81e14 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -1,13 +1,16 @@ package size import ( + "context" "fmt" + "os" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" ) // SizeOrder is responsible for ensuring that the order size is within config limits @@ -22,22 +25,61 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if !ok { return nil, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) } + if fde := o.GetFillDependentEvent(); fde != nil && fde.MatchOrderAmount() { + hello, err := cs.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ + CalculateOffline: true, + CollateralCurrency: o.Pair().Base, + Asset: fde.GetAssetType(), + Side: gctorder.Short, + USDPrice: fde.GetClosePrice(), + IsForNewPosition: true, + FreeCollateral: amountAvailable, + }) + initialAmount := amountAvailable.Mul(hello.Weighting).Div(fde.GetClosePrice()) + oNotionalPosition := initialAmount.Mul(o.GetClosePrice()) + sizedAmount, err := s.calculateAmount(o.GetDirection(), o.GetClosePrice(), oNotionalPosition, cs, o) + if err != nil { + return retOrder, err + } + scaledCollateralFromAmount := sizedAmount.Mul(hello.Weighting) + excess := amountAvailable.Sub(sizedAmount).Add(scaledCollateralFromAmount) + if excess.IsNegative() { + os.Exit(-1) + } + retOrder.SetAmount(sizedAmount) + fde.SetAmount(sizedAmount) + retOrder.FillDependentEvent = fde + log.Infof(common.Backtester, "%v %v", hello.CollateralContribution, err) + log.Infof(common.Backtester, "%v %v", hello, err) + return retOrder, nil + } + + amount, err := s.calculateAmount(retOrder.Direction, retOrder.ClosePrice, amountAvailable, cs, o) + if err != nil { + return retOrder, err + } + retOrder.SetAmount(amount) + + return retOrder, nil +} + +func (s *Size) calculateAmount(direction gctorder.Side, closePrice, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (decimal.Decimal, error) { var amount decimal.Decimal var err error - switch retOrder.GetDirection() { + switch direction { case common.ClosePosition: amount = amountAvailable case gctorder.Buy, gctorder.Long: // check size against currency specific settings - amount, err = s.calculateBuySize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) + amount, err = s.calculateBuySize(closePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) if err != nil { - return nil, err + return decimal.Decimal{}, err } // check size against portfolio specific settings var portfolioSize decimal.Decimal - portfolioSize, err = s.calculateBuySize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) + portfolioSize, err = s.calculateBuySize(closePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) if err != nil { - return nil, err + return decimal.Decimal{}, err } // global settings overrule individual currency settings if amount.GreaterThan(portfolioSize) { @@ -45,28 +87,33 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc } case gctorder.Sell, gctorder.Short: // check size against currency specific settings - amount, err = s.calculateSellSize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) + amount, err = s.calculateSellSize(closePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) if err != nil { - return nil, err + return decimal.Decimal{}, err } // check size against portfolio specific settings - portfolioSize, err := s.calculateSellSize(retOrder.ClosePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) + portfolioSize, err := s.calculateSellSize(closePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) if err != nil { - return nil, err + return decimal.Decimal{}, err } // global settings overrule individual currency settings if amount.GreaterThan(portfolioSize) { amount = portfolioSize } default: - return retOrder, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) + return decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } - if amount.LessThanOrEqual(decimal.Zero) { - return retOrder, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) + if o.GetAmount().IsPositive() { + setAmountSize := o.GetAmount().Mul(closePrice) + if setAmountSize.LessThan(amount) { + amount = setAmountSize + } } - retOrder.SetAmount(amount) - return retOrder, nil + if amount.LessThanOrEqual(decimal.Zero) { + return decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) + } + return amount, nil } // calculateBuySize respects config rules and calculates the amount of money diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index 943c5e14c93..5b5e66ff6b0 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -5,10 +5,7 @@ import ( "testing" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" - gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) func TestSizingAccuracy(t *testing.T) { @@ -171,6 +168,7 @@ func TestCalculateSellSize(t *testing.T) { } } +/* func TestSizeOrder(t *testing.T) { t.Parallel() s := Size{} @@ -223,3 +221,26 @@ func TestSizeOrder(t *testing.T) { t.Error(err) } } + + +*/ +func TestFuckYou(t *testing.T) { + t.Parallel() + weight := 0.95 + //availableFunds := 438.0 + spotPrice := 39122.0 + futurePrice := 43195.0 + + for i := 1.0; i < 100000.0; i++ { + step1 := (i * weight) / futurePrice + futuresNotionalPosition := step1 * futurePrice + spotNotionalPosition := step1 * spotPrice + spotCollateralSize := spotNotionalPosition * weight + extraFundingRequired := futuresNotionalPosition - spotCollateralSize + excess := i - spotNotionalPosition + leftover := excess - extraFundingRequired + t.Log(leftover) + } + + //t.Logf("step1 %v\n futuresNotionalPosition %v\n spotNotionalPosition %v\n spotCollateralSize %v\n excess %v\n extraFundingRequired %v\n leftover %v\n", step1, futuresNotionalPosition, spotNotionalPosition, spotCollateralSize, excess, extraFundingRequired, leftover) +} diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index b4fddea580f..3d614b5c0d0 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -125,6 +125,7 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS futuresSignal.SetPrice(futuresSignal.ClosePrice) futuresSignal.AppendReason("Shorting to perform cash and carry") futuresSignal.CollateralCurrency = spotSignal.CurrencyPair.Base + futuresSignal.MatchesOrderAmount = true spotSignal.AppendReasonf("Signalling shorting of %v", futuresSignal.Pair()) // set the FillDependentEvent to use the futures signal // as the futures signal relies on a completed spot order purchase @@ -134,10 +135,11 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS response = append(response, spotSignal) case pos[len(pos)-1].Status == order.Open && isLastEvent: + spotSignal.SetDirection(common.ClosePosition) + spotSignal.AppendReason("Selling asset on last event") futuresSignal.SetDirection(common.ClosePosition) futuresSignal.AppendReason("Closing position on last event") - spotSignal.SetDirection(order.Sell) - response = append(response, spotSignal, futuresSignal) + response = append(response, futuresSignal, spotSignal) case pos[len(pos)-1].Status == order.Open && diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): futuresSignal.SetDirection(common.ClosePosition) diff --git a/backtester/eventtypes/signal/signal.go b/backtester/eventtypes/signal/signal.go index 4a65d86ebca..47797069fe5 100644 --- a/backtester/eventtypes/signal/signal.go +++ b/backtester/eventtypes/signal/signal.go @@ -86,3 +86,9 @@ func (s *Signal) GetCollateralCurrency() currency.Code { func (s *Signal) IsNil() bool { return s == nil } + +// MatchOrderAmount ensures an order must match +// its set amount or fail +func (s *Signal) MatchOrderAmount() bool { + return s.MatchesOrderAmount +} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index 4feea2de952..f0a120b1d50 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -21,6 +21,8 @@ type Event interface { GetAmount() decimal.Decimal GetFillDependentEvent() Event GetCollateralCurrency() currency.Code + SetAmount(decimal.Decimal) + MatchOrderAmount() bool IsNil() bool } @@ -56,4 +58,7 @@ type Signal struct { // eg with $5000 usd and 1 BTC, specifying BTC ensures // the USD value won't be utilised when sizing an order CollateralCurrency currency.Code + // MatchOrderAmount flags to other event handlers + // that the order amount must match the set Amount property + MatchesOrderAmount bool } diff --git a/backtester/funding/spotpair.go b/backtester/funding/spotpair.go index b15b55b002e..7846c900675 100644 --- a/backtester/funding/spotpair.go +++ b/backtester/funding/spotpair.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -98,7 +99,7 @@ func (p *SpotPair) CanPlaceOrder(side order.Side) bool { switch side { case order.Buy, order.Bid: return p.quote.CanPlaceOrder() - case order.Sell, order.Ask: + case order.Sell, order.Ask, common.ClosePosition: return p.base.CanPlaceOrder() } return false From 21d55abd10718518061d657279d6847b69739801 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 4 May 2022 09:30:02 +1000 Subject: [PATCH 136/171] fixes order sell sizing --- backtester/eventhandlers/exchange/exchange.go | 58 ++++++++++--------- .../portfolio/holdings/holdings.go | 4 +- .../eventhandlers/portfolio/portfolio.go | 49 ++++++---------- .../statistics/currencystatistics.go | 4 +- 4 files changed, 55 insertions(+), 60 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 319519924fb..8790ab0bac2 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -118,9 +118,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } if amount.LessThanOrEqual(decimal.Zero) && f.GetAmount().GreaterThan(decimal.Zero) { switch f.GetDirection() { - case gctorder.Buy: + case gctorder.Buy, gctorder.Bid: f.SetDirection(common.CouldNotBuy) - case gctorder.Sell: + case gctorder.Sell, gctorder.Ask: f.SetDirection(common.CouldNotSell) case gctorder.Short: f.SetDirection(common.CouldNotShort) @@ -204,14 +204,14 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro switch f.AssetType { case asset.Spot: - pr, fundErr := funds.PairReleaser() - if fundErr != nil { - return fundErr + pr, err := funds.PairReleaser() + if err != nil { + return err } if orderError != nil { - fundErr = pr.Release(eventFunds, eventFunds, f.GetDirection()) - if fundErr != nil { - f.AppendReason(fundErr.Error()) + err = pr.Release(eventFunds, eventFunds, f.GetDirection()) + if err != nil { + f.AppendReason(err.Error()) } if f.GetDirection() == gctorder.Buy { f.SetDirection(common.CouldNotBuy) @@ -221,30 +221,36 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro return orderError } switch f.GetDirection() { - case gctorder.Buy: - fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) - if fundErr != nil { - return fundErr + case gctorder.Buy, gctorder.Bid: + err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) + if err != nil { + return err + } + err = pr.IncreaseAvailable(limitReducedAmount, f.GetDirection()) + if err != nil { + return err + } + case gctorder.Sell, gctorder.Ask: + err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) + if err != nil { + return err } - pr.IncreaseAvailable(limitReducedAmount, f.GetDirection()) - case gctorder.Sell: - fundErr = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) - if fundErr != nil { - return fundErr + err = pr.IncreaseAvailable(limitReducedAmount.Mul(adjustedPrice), f.GetDirection()) + if err != nil { + return err } - pr.IncreaseAvailable(limitReducedAmount.Mul(adjustedPrice), f.GetDirection()) default: return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.GetDirection()) } case asset.Futures: - cr, fundErr := funds.CollateralReleaser() - if fundErr != nil { - return fundErr + cr, err := funds.CollateralReleaser() + if err != nil { + return err } if orderError != nil { - fundErr = cr.ReleaseContracts(orderAmount) - if fundErr != nil { - return fundErr + err = cr.ReleaseContracts(orderAmount) + if err != nil { + return err } switch f.GetDirection() { case gctorder.Short: @@ -274,10 +280,10 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c var minMax MinMax var direction gctorder.Side switch f.GetDirection() { - case gctorder.Buy: + case gctorder.Buy, gctorder.Bid: minMax = cs.BuySide direction = common.CouldNotBuy - case gctorder.Sell: + case gctorder.Sell, gctorder.Ask: minMax = cs.SellSide direction = common.CouldNotSell case gctorder.Long: diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index 077a8db064f..f77dedfe1d9 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -83,10 +83,10 @@ func (h *Holding) update(e fill.Event, f funding.IPairReader) { h.BaseValue = h.BaseSize.Mul(price) h.TotalFees = h.TotalFees.Add(fee) switch direction { - case order.Buy: + case order.Buy, order.Bid: h.BoughtAmount = h.BoughtAmount.Add(amount) h.BoughtValue = h.BoughtAmount.Mul(price) - case order.Sell: + case order.Sell, order.Ask: h.SoldAmount = h.SoldAmount.Add(amount) h.SoldValue = h.SoldAmount.Mul(price) case common.DoNothing, common.CouldNotSell, common.CouldNotBuy, common.MissingData, common.TransferredFunds, "": diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b95c409bc0a..b2535bd6d5b 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -85,17 +85,18 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi var sizingFunds decimal.Decimal var side = ev.GetDirection() if ev.GetAssetType() == asset.Spot { - if ev.GetDirection() == common.ClosePosition { + if side == common.ClosePosition { side = gctorder.Sell } pReader, err := funds.GetPairReader() if err != nil { return nil, err } - if ev.GetDirection() == gctorder.Sell { - sizingFunds = pReader.BaseAvailable() - } else { + switch side { + case gctorder.Buy, gctorder.Bid: sizingFunds = pReader.QuoteAvailable() + case gctorder.Sell, gctorder.Ask: + sizingFunds = pReader.BaseAvailable() } } else if ev.GetAssetType().IsFutures() { if ev.GetDirection() == common.ClosePosition { @@ -141,10 +142,10 @@ func cannotPurchase(ev signal.Event, o *order.Order) (*order.Order, error) { } o.AppendReason(notEnoughFundsTo + " " + ev.GetDirection().Lower()) switch ev.GetDirection() { - case gctorder.Sell: - o.SetDirection(common.CouldNotSell) - case gctorder.Buy: + case gctorder.Buy, gctorder.Bid: o.SetDirection(common.CouldNotBuy) + case gctorder.Sell, gctorder.Ask: + o.SetDirection(common.CouldNotSell) case gctorder.Short: o.SetDirection(common.CouldNotShort) case gctorder.Long: @@ -188,12 +189,11 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, ev func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, originalOrderSignal *order.Order, sizingFunds decimal.Decimal, funds funding.IFundReserver) *order.Order { sizedOrder, err := p.sizeManager.SizeOrder(originalOrderSignal, sizingFunds, cs) - if err != nil { - originalOrderSignal.AppendReason(err.Error()) + if err != nil || sizedOrder.Amount.IsZero() { switch originalOrderSignal.Direction { - case gctorder.Buy: + case gctorder.Buy, gctorder.Bid: originalOrderSignal.Direction = common.CouldNotBuy - case gctorder.Sell: + case gctorder.Sell, gctorder.Ask: originalOrderSignal.Direction = common.CouldNotSell case gctorder.Long: originalOrderSignal.Direction = common.CouldNotLong @@ -203,27 +203,17 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi originalOrderSignal.Direction = common.DoNothing } d.SetDirection(originalOrderSignal.Direction) - return originalOrderSignal - } - - if sizedOrder.Amount.IsZero() { - switch originalOrderSignal.Direction { - case gctorder.Buy: - originalOrderSignal.Direction = common.CouldNotBuy - case gctorder.Sell: - originalOrderSignal.Direction = common.CouldNotSell - case gctorder.Long: - originalOrderSignal.Direction = common.CouldNotLong - case gctorder.Short: - originalOrderSignal.Direction = common.CouldNotShort - default: - originalOrderSignal.Direction = common.DoNothing + if err != nil { + originalOrderSignal.AppendReason(err.Error()) + return originalOrderSignal } - d.SetDirection(originalOrderSignal.Direction) originalOrderSignal.AppendReason("sized order to 0") } switch d.GetDirection() { - case gctorder.Sell: + case gctorder.Buy, gctorder.Bid: + err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice), gctorder.Buy) + sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice) + case gctorder.Sell, gctorder.Ask: err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) sizedOrder.AllocatedSize = sizedOrder.Amount case gctorder.Short, gctorder.Long: @@ -238,8 +228,7 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi sizedOrder.AllocatedSize = sizedOrder.Amount } default: - err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice), gctorder.Buy) - sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice) + err = errInvalidDirection } if err != nil { sizedOrder.Direction = common.DoNothing diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 85d4e5ed736..81bb7527a1a 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -22,9 +22,9 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e lastPrice := last.ClosePrice for i := range last.Transactions.Orders { switch last.Transactions.Orders[i].Order.Side { - case gctorder.Buy: + case gctorder.Buy, gctorder.Bid: c.BuyOrders++ - case gctorder.Sell: + case gctorder.Sell, gctorder.Ask: c.SellOrders++ case gctorder.Long: c.LongOrders++ From 18c74d38736a44bcf4ff7e2c7b40441fa6a3b8b7 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 4 May 2022 11:37:32 +1000 Subject: [PATCH 137/171] Adds tests to sizing. Fixes charting issue --- backtester/eventhandlers/exchange/exchange.go | 9 +-- .../eventhandlers/portfolio/size/size.go | 20 +++--- .../eventhandlers/portfolio/size/size_test.go | 65 ++++++++++++------- backtester/funding/spotpair.go | 6 +- backtester/report/chart.go | 19 ++++-- backtester/report/chart_test.go | 5 +- 6 files changed, 77 insertions(+), 47 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 8790ab0bac2..8dd90e694a5 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -163,7 +163,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if err != nil { return f, err } - if !o.IsClosingPosition() && !o.IsLiquidating() { + if !o.IsLiquidating() { err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) if err != nil { return f, err @@ -213,9 +213,10 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro if err != nil { f.AppendReason(err.Error()) } - if f.GetDirection() == gctorder.Buy { + switch f.GetDirection() { + case gctorder.Buy, gctorder.Bid: f.SetDirection(common.CouldNotBuy) - } else if f.GetDirection() == gctorder.Sell { + case gctorder.Sell, gctorder.Ask, common.ClosePosition: f.SetDirection(common.CouldNotSell) } return orderError @@ -230,7 +231,7 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro if err != nil { return err } - case gctorder.Sell, gctorder.Ask: + case gctorder.Sell, gctorder.Ask, common.ClosePosition: err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) if err != nil { return err diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 31f65e81e14..a14d5456fbd 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -3,14 +3,12 @@ package size import ( "context" "fmt" - "os" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/log" ) // SizeOrder is responsible for ensuring that the order size is within config limits @@ -26,7 +24,7 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc return nil, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) } if fde := o.GetFillDependentEvent(); fde != nil && fde.MatchOrderAmount() { - hello, err := cs.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ + scalingInfo, err := cs.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ CalculateOffline: true, CollateralCurrency: o.Pair().Base, Asset: fde.GetAssetType(), @@ -35,28 +33,28 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc IsForNewPosition: true, FreeCollateral: amountAvailable, }) - initialAmount := amountAvailable.Mul(hello.Weighting).Div(fde.GetClosePrice()) + if err != nil { + return nil, err + } + initialAmount := amountAvailable.Mul(scalingInfo.Weighting).Div(fde.GetClosePrice()) oNotionalPosition := initialAmount.Mul(o.GetClosePrice()) sizedAmount, err := s.calculateAmount(o.GetDirection(), o.GetClosePrice(), oNotionalPosition, cs, o) if err != nil { - return retOrder, err + return nil, err } - scaledCollateralFromAmount := sizedAmount.Mul(hello.Weighting) + scaledCollateralFromAmount := sizedAmount.Mul(scalingInfo.Weighting) excess := amountAvailable.Sub(sizedAmount).Add(scaledCollateralFromAmount) if excess.IsNegative() { - os.Exit(-1) + return nil, fmt.Errorf("%w not enough funding for position", errCannotAllocate) } retOrder.SetAmount(sizedAmount) fde.SetAmount(sizedAmount) - retOrder.FillDependentEvent = fde - log.Infof(common.Backtester, "%v %v", hello.CollateralContribution, err) - log.Infof(common.Backtester, "%v %v", hello, err) return retOrder, nil } amount, err := s.calculateAmount(retOrder.Direction, retOrder.ClosePrice, amountAvailable, cs, o) if err != nil { - return retOrder, err + return nil, err } retOrder.SetAmount(amount) diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index 5b5e66ff6b0..f8583af596b 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -1,11 +1,21 @@ package size import ( + "context" "errors" "testing" + "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/ftx" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) func TestSizingAccuracy(t *testing.T) { @@ -168,7 +178,6 @@ func TestCalculateSellSize(t *testing.T) { } } -/* func TestSizeOrder(t *testing.T) { t.Parallel() s := Size{} @@ -176,7 +185,16 @@ func TestSizeOrder(t *testing.T) { if !errors.Is(err, common.ErrNilArguments) { t.Error(err) } - o := &order.Order{} + o := &order.Order{ + Base: event.Base{ + Offset: 1, + Exchange: "ftx", + Time: time.Now(), + CurrencyPair: currency.NewPair(currency.BTC, currency.USD), + UnderlyingPair: currency.NewPair(currency.BTC, currency.USD), + AssetType: asset.Spot, + }, + } cs := &exchange.Settings{} _, err = s.SizeOrder(o, decimal.Zero, cs) if !errors.Is(err, errNoFunds) { @@ -220,27 +238,28 @@ func TestSizeOrder(t *testing.T) { if err != nil { t.Error(err) } -} + // spot futures sizing + o.FillDependentEvent = &signal.Signal{ + Base: o.Base, + MatchesOrderAmount: true, + ClosePrice: decimal.NewFromInt(1337), + } + exch := ftx.FTX{} + err = exch.LoadCollateralWeightings(context.Background()) + if err != nil { + t.Error(err) + } + cs.Exchange = &exch + _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + if err != nil { + t.Error(err) + } + + o.ClosePrice = decimal.NewFromInt(1000000000) + _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received: %v, expected: %v", err, errCannotAllocate) + } -*/ -func TestFuckYou(t *testing.T) { - t.Parallel() - weight := 0.95 - //availableFunds := 438.0 - spotPrice := 39122.0 - futurePrice := 43195.0 - - for i := 1.0; i < 100000.0; i++ { - step1 := (i * weight) / futurePrice - futuresNotionalPosition := step1 * futurePrice - spotNotionalPosition := step1 * spotPrice - spotCollateralSize := spotNotionalPosition * weight - extraFundingRequired := futuresNotionalPosition - spotCollateralSize - excess := i - spotNotionalPosition - leftover := excess - extraFundingRequired - t.Log(leftover) - } - - //t.Logf("step1 %v\n futuresNotionalPosition %v\n spotNotionalPosition %v\n spotCollateralSize %v\n excess %v\n extraFundingRequired %v\n leftover %v\n", step1, futuresNotionalPosition, spotNotionalPosition, spotCollateralSize, excess, extraFundingRequired, leftover) } diff --git a/backtester/funding/spotpair.go b/backtester/funding/spotpair.go index 7846c900675..d5ff7d61e6e 100644 --- a/backtester/funding/spotpair.go +++ b/backtester/funding/spotpair.go @@ -45,7 +45,7 @@ func (p *SpotPair) Reserve(amount decimal.Decimal, side order.Side) error { switch side { case order.Buy, order.Bid: return p.quote.Reserve(amount) - case order.Sell, order.Ask: + case order.Sell, order.Ask, common.ClosePosition: return p.base.Reserve(amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", @@ -64,7 +64,7 @@ func (p *SpotPair) Release(amount, diff decimal.Decimal, side order.Side) error switch side { case order.Buy, order.Bid: return p.quote.Release(amount, diff) - case order.Sell, order.Ask: + case order.Sell, order.Ask, common.ClosePosition: return p.base.Release(amount, diff) } return fmt.Errorf("%w for %v %v %v. Unknown side %v", @@ -81,7 +81,7 @@ func (p *SpotPair) IncreaseAvailable(amount decimal.Decimal, side order.Side) er switch side { case order.Buy, order.Bid: return p.base.IncreaseAvailable(amount) - case order.Sell, order.Ask: + case order.Sell, order.Ask, common.ClosePosition: return p.quote.IncreaseAvailable(amount) } return fmt.Errorf("%w for %v %v %v. Unknown side %v", diff --git a/backtester/report/chart.go b/backtester/report/chart.go index 8e381b80b5d..41b2d2a0aa3 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -128,19 +128,28 @@ func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pai } currs := make(map[currency.Pair]linkCurrencyDiff) response := &Chart{} + for _, assetMap := range items { for item, pairMap := range assetMap { for pair, result := range pairMap { - diff, ok := currs[pair] - if !ok { - diff = linkCurrencyDiff{} - } if item.IsFutures() { + p := result.UnderlyingPair.Format("", true) + diff, ok := currs[p] + if !ok { + diff = linkCurrencyDiff{} + } diff.FuturesPair = pair - diff.SpotPair = result.UnderlyingPair + diff.SpotPair = p diff.FuturesEvents = result.Events + currs[p] = diff } else { + p := pair.Format("", true) + diff, ok := currs[p] + if !ok { + diff = linkCurrencyDiff{} + } diff.SpotEvents = result.Events + currs[p] = diff } } } diff --git a/backtester/report/chart_test.go b/backtester/report/chart_test.go index 53087ff881a..f53066341ab 100644 --- a/backtester/report/chart_test.go +++ b/backtester/report/chart_test.go @@ -213,8 +213,11 @@ func TestCreateFuturesSpotDiffChart(t *testing.T) { }, } - _, err = createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) + charty, err := createFuturesSpotDiffChart(d.Statistics.ExchangeAssetPairStatistics) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } + if len(charty.Data) == 0 { + t.Error("expected data") + } } From 6eeba337e74091cc6eaa268e013fdea0ed5c302b Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 4 May 2022 11:52:57 +1000 Subject: [PATCH 138/171] clint splintered the linters with flint --- .../eventhandlers/portfolio/size/size_test.go | 1 - backtester/funding/spotpair_test.go | 25 +++++++++++++++---- exchanges/exchange.go | 2 +- exchanges/order/futures_test.go | 8 +++--- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index f8583af596b..80ca27ccf18 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -261,5 +261,4 @@ func TestSizeOrder(t *testing.T) { if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) } - } diff --git a/backtester/funding/spotpair_test.go b/backtester/funding/spotpair_test.go index b54faecb3fa..138625a6d11 100644 --- a/backtester/funding/spotpair_test.go +++ b/backtester/funding/spotpair_test.go @@ -191,25 +191,40 @@ func TestIncreaseAvailablePair(t *testing.T) { baseItem.pairedWith = quoteItem quoteItem.pairedWith = baseItem pairItems := SpotPair{base: baseItem, quote: quoteItem} - pairItems.IncreaseAvailable(decimal.Zero, gctorder.Buy) + err = pairItems.IncreaseAvailable(decimal.Zero, gctorder.Buy) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } if !pairItems.quote.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) } - pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) + err = pairItems.IncreaseAvailable(decimal.Zero, gctorder.Sell) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } if !pairItems.base.available.IsZero() { t.Errorf("received '%v' expected '%v'", decimal.Zero, pairItems.base.available) } - pairItems.IncreaseAvailable(elite.Neg(), gctorder.Sell) + err = pairItems.IncreaseAvailable(elite.Neg(), gctorder.Sell) + if !errors.Is(err, errZeroAmountReceived) { + t.Errorf("received '%v' expected '%v'", err, errZeroAmountReceived) + } if !pairItems.quote.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.quote.available) } - pairItems.IncreaseAvailable(elite, gctorder.Buy) + err = pairItems.IncreaseAvailable(elite, gctorder.Buy) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if !pairItems.base.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) } - pairItems.IncreaseAvailable(elite, common.DoNothing) + err = pairItems.IncreaseAvailable(elite, common.DoNothing) + if !errors.Is(err, errCannotAllocate) { + t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) + } if !pairItems.base.available.Equal(elite) { t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index bd60e749f40..d949f8d3f59 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1312,7 +1312,7 @@ func (b *Base) GetCollateralCurrencyForContract(asset.Item, currency.Pair) (curr func (b *Base) GetCurrencyForRealisedPNL(_ asset.Item, _ currency.Pair) (currency.Code, asset.Item, error) { return currency.Code{}, asset.Empty, common.ErrNotYetImplemented } - + // HasAssetTypeAccountSegregation returns if the accounts are divided into asset // types instead of just being denoted as spot holdings. func (b *Base) HasAssetTypeAccountSegregation() bool { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 9fc35ee1b63..b9b17e0aae0 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -990,10 +990,10 @@ func TestMPTLiquidate(t *testing.T) { } if e.positions[0].status != Liquidated { - t.Errorf("recieved '%v' expected '%v'", e.positions[0].status, Liquidated) + t.Errorf("received '%v' expected '%v'", e.positions[0].status, Liquidated) } if !e.positions[0].exposure.IsZero() { - t.Errorf("recieved '%v' expected '%v'", e.positions[0].exposure, 0) + t.Errorf("received '%v' expected '%v'", e.positions[0].exposure, 0) } e = nil @@ -1045,10 +1045,10 @@ func TestPositionLiquidate(t *testing.T) { } if p.status != Liquidated { - t.Errorf("recieved '%v' expected '%v'", p.status, Liquidated) + t.Errorf("received '%v' expected '%v'", p.status, Liquidated) } if !p.exposure.IsZero() { - t.Errorf("recieved '%v' expected '%v'", p.exposure, 0) + t.Errorf("received '%v' expected '%v'", p.exposure, 0) } p = nil From 25ce60d6fca22a4b45ae778d9aac5075868e69d9 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 5 May 2022 17:11:38 +1000 Subject: [PATCH 139/171] Improves stats, stat rendering --- backtester/config/config.go | 14 ++++- backtester/config/config_types.go | 6 +- .../config/examples/ftx-cash-carry.strat | 6 +- backtester/engine/setup.go | 5 ++ backtester/eventhandlers/exchange/exchange.go | 2 - .../portfolio/holdings/holdings.go | 59 ++++++++++++++----- .../portfolio/holdings/holdings_test.go | 16 ++--- .../portfolio/holdings/holdings_types.go | 7 ++- .../eventhandlers/portfolio/portfolio.go | 43 +++++++------- .../statistics/currencystatistics.go | 34 ++++++++--- .../statistics/fundingstatistics.go | 3 - .../eventhandlers/statistics/printresults.go | 20 +++---- .../eventhandlers/statistics/statistics.go | 2 +- .../statistics/statistics_test.go | 4 +- .../statistics/statistics_types.go | 5 -- backtester/report/tpl.gohtml | 58 +++--------------- 16 files changed, 147 insertions(+), 137 deletions(-) diff --git a/backtester/config/config.go b/backtester/config/config.go index bdd6e9f432d..693bc1550e0 100644 --- a/backtester/config/config.go +++ b/backtester/config/config.go @@ -269,12 +269,20 @@ func (c *Config) PrintSetting() { } } if c.CurrencySettings[i].TakerFee != nil { - log.Infof(common.Config, "Taker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) + if c.CurrencySettings[i].UsingExchangeTakerFee { + log.Infof(common.Config, "Taker fee: Using Exchange's API default taker rate: %v", c.CurrencySettings[i].TakerFee.Round(8)) + } else { + log.Infof(common.Config, "Taker fee: %v", c.CurrencySettings[i].TakerFee.Round(8)) + } } if c.CurrencySettings[i].MakerFee != nil { - log.Infof(common.Config, "Maker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) + if c.CurrencySettings[i].UsingExchangeMakerFee { + log.Infof(common.Config, "Maker fee: Using Exchange's API default maker rate: %v", c.CurrencySettings[i].MakerFee.Round(8)) + } else { + log.Infof(common.Config, "Maker fee: %v", c.CurrencySettings[i].MakerFee.Round(8)) + } } - log.Infof(common.Config, "Minimum slippage percent %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) + log.Infof(common.Config, "Minimum slippage percent: %v", c.CurrencySettings[i].MinimumSlippagePercent.Round(8)) log.Infof(common.Config, "Maximum slippage percent: %v", c.CurrencySettings[i].MaximumSlippagePercent.Round(8)) log.Infof(common.Config, "Buy rules: %+v", c.CurrencySettings[i].BuySide) log.Infof(common.Config, "Sell rules: %+v", c.CurrencySettings[i].SellSide) diff --git a/backtester/config/config_types.go b/backtester/config/config_types.go index 1aa4b5c0a27..ace68ab62a8 100644 --- a/backtester/config/config_types.go +++ b/backtester/config/config_types.go @@ -143,8 +143,10 @@ type CurrencySettings struct { MinimumSlippagePercent decimal.Decimal `json:"min-slippage-percent"` MaximumSlippagePercent decimal.Decimal `json:"max-slippage-percent"` - MakerFee *decimal.Decimal `json:"maker-fee-override,omitempty"` - TakerFee *decimal.Decimal `json:"taker-fee-override,omitempty"` + UsingExchangeMakerFee bool `json:"-"` + MakerFee *decimal.Decimal `json:"maker-fee-override,omitempty"` + UsingExchangeTakerFee bool `json:"-"` + TakerFee *decimal.Decimal `json:"taker-fee-override,omitempty"` MaximumHoldingsRatio decimal.Decimal `json:"maximum-holdings-ratio"` SkipCandleVolumeFitting bool `json:"skip-candle-volume-fitting"` diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 41394608b1e..4aac187c764 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -36,8 +36,9 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", + "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", - "skip-candle-volume-fitting": true, + "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, "use-exchange-pnl-calculation": false }, @@ -58,8 +59,9 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", + "maker-fee-override": "0.001", "maximum-holdings-ratio": "0", - "skip-candle-volume-fitting": true, + "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, "use-exchange-pnl-calculation": false } diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index e0111d6def1..acf32d11046 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -465,9 +465,14 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange apiMakerFee, apiTakerFee = getFees(context.TODO(), exch, pair) if cfg.CurrencySettings[i].MakerFee == nil { makerFee = apiMakerFee + cfg.CurrencySettings[i].MakerFee = &makerFee + cfg.CurrencySettings[i].UsingExchangeMakerFee = true + } if cfg.CurrencySettings[i].TakerFee == nil { takerFee = apiTakerFee + cfg.CurrencySettings[i].TakerFee = &takerFee + cfg.CurrencySettings[i].UsingExchangeTakerFee = true } } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 8dd90e694a5..f76176eabce 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -20,7 +20,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/log" ) // Reset returns the exchange to initial settings @@ -381,7 +380,6 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal Cost: p, FullyMatched: true, } - log.Infof(log.Global, "%v %v %v %v %v - %v %v", f.GetOffset(), f.GetExchange(), f.GetAssetType(), f.Pair(), f.GetTime(), price, amount) resp, err := orderManager.SubmitFakeOrder(o, submitResponse, useExchangeLimits) if resp != nil { orderID = resp.OrderID diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index f77dedfe1d9..baa4449d0cd 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -58,10 +58,10 @@ func Create(ev ClosePriceReader, fundReader funding.IFundReader) (Holding, error } // Update calculates holding statistics for the events time -func (h *Holding) Update(e fill.Event, f funding.IPairReader) { +func (h *Holding) Update(e fill.Event, f funding.IFundReader) error { h.Timestamp = e.GetTime() h.Offset = e.GetOffset() - h.update(e, f) + return h.update(e, f) } // UpdateValue calculates the holding's value for a data event's time and price @@ -72,45 +72,72 @@ func (h *Holding) UpdateValue(d common.DataEventHandler) { h.updateValue(latest) } -func (h *Holding) update(e fill.Event, f funding.IPairReader) { +func (h *Holding) update(e fill.Event, f funding.IFundReader) error { direction := e.GetDirection() if o := e.GetOrder(); o != nil { amount := decimal.NewFromFloat(o.Amount) fee := decimal.NewFromFloat(o.Fee) price := decimal.NewFromFloat(o.Price) - h.BaseSize = f.BaseAvailable() - h.QuoteSize = f.QuoteAvailable() + a := e.GetAssetType() + switch { + case a == asset.Spot: + spotR, err := f.GetPairReader() + if err != nil { + return err + } + h.BaseSize = spotR.BaseAvailable() + h.QuoteSize = spotR.QuoteAvailable() + case a.IsFutures(): + collat, err := f.GetCollateralReader() + if err != nil { + return err + } + h.BaseSize = collat.CurrentHoldings() + h.QuoteSize = collat.AvailableFunds() + default: + return fmt.Errorf("%v %w", a, asset.ErrNotSupported) + } + h.BaseValue = h.BaseSize.Mul(price) h.TotalFees = h.TotalFees.Add(fee) + if e.GetAssetType().IsFutures() { + // responsibility of tracking futures orders is + // with order.PositionTracker + return nil + } switch direction { - case order.Buy, order.Bid: + case order.Buy, + order.Bid: h.BoughtAmount = h.BoughtAmount.Add(amount) - h.BoughtValue = h.BoughtAmount.Mul(price) - case order.Sell, order.Ask: + h.ScaledBoughtValue = h.BoughtAmount.Mul(price) + h.CommittedFunds = h.CommittedFunds.Add(amount.Mul(price)) + case order.Sell, + order.Ask: h.SoldAmount = h.SoldAmount.Add(amount) - h.SoldValue = h.SoldAmount.Mul(price) - case common.DoNothing, common.CouldNotSell, common.CouldNotBuy, common.MissingData, common.TransferredFunds, "": + h.ScaledSoldValue = h.SoldAmount.Mul(price) + h.CommittedFunds = h.CommittedFunds.Sub(amount.Mul(price)) } } h.TotalValueLostToVolumeSizing = h.TotalValueLostToVolumeSizing.Add(e.GetClosePrice().Sub(e.GetVolumeAdjustedPrice()).Mul(e.GetAmount())) h.TotalValueLostToSlippage = h.TotalValueLostToSlippage.Add(e.GetVolumeAdjustedPrice().Sub(e.GetPurchasePrice()).Mul(e.GetAmount())) h.updateValue(e.GetClosePrice()) + return nil } func (h *Holding) updateValue(latestPrice decimal.Decimal) { origPosValue := h.BaseValue - origBoughtValue := h.BoughtValue - origSoldValue := h.SoldValue + origBoughtValue := h.ScaledBoughtValue + origSoldValue := h.ScaledSoldValue origTotalValue := h.TotalValue h.BaseValue = h.BaseSize.Mul(latestPrice) - h.BoughtValue = h.BoughtAmount.Mul(latestPrice) - h.SoldValue = h.SoldAmount.Mul(latestPrice) + h.ScaledBoughtValue = h.BoughtAmount.Mul(latestPrice) + h.ScaledSoldValue = h.SoldAmount.Mul(latestPrice) h.TotalValue = h.BaseValue.Add(h.QuoteSize) h.TotalValueDifference = h.TotalValue.Sub(origTotalValue) - h.BoughtValueDifference = h.BoughtValue.Sub(origBoughtValue) + h.BoughtValueDifference = h.ScaledBoughtValue.Sub(origBoughtValue) h.PositionsValueDifference = h.BaseValue.Sub(origPosValue) - h.SoldValueDifference = h.SoldValue.Sub(origSoldValue) + h.SoldValueDifference = h.ScaledSoldValue.Sub(origSoldValue) if !origTotalValue.IsZero() { h.ChangeInTotalValuePercent = h.TotalValue.Sub(origTotalValue).Div(origTotalValue) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index 8ad3bc80ad2..b19a6f9b251 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -182,8 +182,8 @@ func TestUpdateBuyStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.BoughtValue.Equal(decimal.NewFromInt(500)) { - t.Errorf("expected '%v' received '%v'", 500, h.BoughtValue) + if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(500)) { + t.Errorf("expected '%v' received '%v'", 500, h.ScaledBoughtValue) } if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) @@ -229,8 +229,8 @@ func TestUpdateBuyStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromFloat(1.5)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.BoughtValue.Equal(decimal.NewFromInt(750)) { - t.Errorf("expected '%v' received '%v'", 750, h.BoughtValue) + if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(750)) { + t.Errorf("expected '%v' received '%v'", 750, h.ScaledBoughtValue) } if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) @@ -311,8 +311,8 @@ func TestUpdateSellStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.BoughtValue.Equal(decimal.NewFromInt(500)) { - t.Errorf("expected '%v' received '%v'", 500, h.BoughtValue) + if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(500)) { + t.Errorf("expected '%v' received '%v'", 500, h.ScaledBoughtValue) } if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) @@ -355,8 +355,8 @@ func TestUpdateSellStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.BoughtValue.Equal(decimal.NewFromInt(500)) { - t.Errorf("expected '%v' received '%v'", 500, h.BoughtValue) + if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(500)) { + t.Errorf("expected '%v' received '%v'", 500, h.ScaledBoughtValue) } if !h.SoldAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.SoldAmount) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_types.go b/backtester/eventhandlers/portfolio/holdings/holdings_types.go index c65cebb567f..8846e8ac915 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_types.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_types.go @@ -29,10 +29,13 @@ type Holding struct { TotalInitialValue decimal.Decimal `json:"total-initial-value"` QuoteSize decimal.Decimal `json:"quote-size"` SoldAmount decimal.Decimal `json:"sold-amount"` + ScaledSoldValue decimal.Decimal `json:"scaled-sold-value"` SoldValue decimal.Decimal `json:"sold-value"` BoughtAmount decimal.Decimal `json:"bought-amount"` - BoughtValue decimal.Decimal `json:"bought-value"` - IsLiquidated bool + ScaledBoughtValue decimal.Decimal `json:"scaled-bought-value"` + CommittedFunds decimal.Decimal `json:"committed-funds"` + + IsLiquidated bool TotalValueDifference decimal.Decimal ChangeInTotalValuePercent decimal.Decimal diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b2535bd6d5b..73a12167419 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -248,34 +248,33 @@ func (p *Portfolio) OnFill(ev fill.Event, funds funding.IFundReleaser) (fill.Eve } var err error - if ev.GetAssetType() == asset.Spot { - var fp funding.IPairReleaser - fp, err = funds.PairReleaser() + // Get the holding from the previous iteration, create it if it doesn't yet have a timestamp + h := lookup.GetHoldingsForTime(ev.GetTime().Add(-ev.GetInterval().Duration())) + if !h.Timestamp.IsZero() { + err = h.Update(ev, funds) if err != nil { return nil, err } - // Get the holding from the previous iteration, create it if it doesn't yet have a timestamp - h := lookup.GetHoldingsForTime(ev.GetTime().Add(-ev.GetInterval().Duration())) - if !h.Timestamp.IsZero() { - h.Update(ev, fp) + } else { + h = lookup.GetLatestHoldings() + if h.Timestamp.IsZero() { + h, err = holdings.Create(ev, funds) + if err != nil { + return nil, err + } } else { - h = lookup.GetLatestHoldings() - if h.Timestamp.IsZero() { - h, err = holdings.Create(ev, funds) - if err != nil { - return nil, err - } - } else { - h.Update(ev, fp) + err = h.Update(ev, funds) + if err != nil { + return nil, err } } - err = p.setHoldingsForOffset(&h, true) - if errors.Is(err, errNoHoldings) { - err = p.setHoldingsForOffset(&h, false) - } - if err != nil { - log.Error(common.Portfolio, err) - } + } + err = p.setHoldingsForOffset(&h, true) + if errors.Is(err, errNoHoldings) { + err = p.setHoldingsForOffset(&h, false) + } + if err != nil { + log.Error(common.Portfolio, err) } err = p.addComplianceSnapshot(ev) diff --git a/backtester/eventhandlers/statistics/currencystatistics.go b/backtester/eventhandlers/statistics/currencystatistics.go index 81bb7527a1a..f670aff3ac5 100644 --- a/backtester/eventhandlers/statistics/currencystatistics.go +++ b/backtester/eventhandlers/statistics/currencystatistics.go @@ -7,6 +7,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" gctcommon "github.com/thrasher-corp/gocryptotrader/common" gctmath "github.com/thrasher-corp/gocryptotrader/common/math" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -54,7 +55,10 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e c.StrategyMovement = last.Holdings.TotalValue.Sub(first.Holdings.TotalValue).Div(first.Holdings.TotalValue).Mul(oneHundred) } c.analysePNLGrowth() - c.calculateHighestCommittedFunds() + err = c.calculateHighestCommittedFunds() + if err != nil { + return err + } returnsPerCandle := make([]decimal.Decimal, len(c.Events)) benchmarkRates := make([]decimal.Decimal, len(c.Events)) @@ -113,7 +117,6 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e } c.IsStrategyProfitable = last.Holdings.TotalValue.GreaterThan(first.Holdings.TotalValue) c.DoesPerformanceBeatTheMarket = c.StrategyMovement.GreaterThan(c.MarketMovement) - c.TotalFees = last.Holdings.TotalFees.Round(8) c.TotalValueLostToVolumeSizing = last.Holdings.TotalValueLostToVolumeSizing.Round(2) c.TotalValueLost = last.Holdings.TotalValueLost.Round(2) @@ -129,14 +132,29 @@ func (c *CurrencyPairStatistic) CalculateResults(riskFreeRate decimal.Decimal) e return nil } -func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() { - for i := range c.Events { - if c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice).GreaterThan(c.HighestCommittedFunds.Value) || !c.HighestCommittedFunds.Set { - c.HighestCommittedFunds.Value = c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice) - c.HighestCommittedFunds.Time = c.Events[i].Holdings.Timestamp - c.HighestCommittedFunds.Set = true +func (c *CurrencyPairStatistic) calculateHighestCommittedFunds() error { + switch { + case c.Asset == asset.Spot: + for i := range c.Events { + if c.Events[i].Holdings.CommittedFunds.GreaterThan(c.HighestCommittedFunds.Value) || !c.HighestCommittedFunds.Set { + c.HighestCommittedFunds.Value = c.Events[i].Holdings.CommittedFunds + c.HighestCommittedFunds.Time = c.Events[i].Time + c.HighestCommittedFunds.Set = true + } + } + case c.Asset.IsFutures(): + for i := range c.Events { + valueAtTime := c.Events[i].Holdings.BaseSize.Mul(c.Events[i].ClosePrice) + if valueAtTime.GreaterThan(c.HighestCommittedFunds.Value) || !c.HighestCommittedFunds.Set { + c.HighestCommittedFunds.Value = valueAtTime + c.HighestCommittedFunds.Time = c.Events[i].Time + c.HighestCommittedFunds.Set = true + } } + default: + return fmt.Errorf("%v %w", c.Asset, asset.ErrNotSupported) } + return nil } func (c *CurrencyPairStatistic) analysePNLGrowth() { diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index dd080746aa0..09c6ecabaee 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -59,9 +59,6 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str if response.Items[i].IsCollateral { continue } - usdStats.TotalOrders += response.Items[i].TotalOrders - usdStats.BuyOrders += response.Items[i].BuyOrders - usdStats.SellOrders += response.Items[i].SellOrders } for i := range report.USDTotalsOverTime { if usdStats.HighestHoldingValue.Value.LessThan(report.USDTotalsOverTime[i].USDValue) { diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index de6e2310388..544bd85d3a4 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -86,7 +86,7 @@ func (s *Statistic) PrintAllEventsChronologically() { colour = common.ColourDarkGrey } msg := fmt.Sprintf(colour+ - "%v %v%v%v| Price: $%v\tDirection: %v", + "%v %v%v%v| Price: %v\tDirection: %v", currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), fSIL(exch, limit12), fSIL(a.String(), limit10), @@ -103,16 +103,16 @@ func (s *Statistic) PrintAllEventsChronologically() { colour = common.ColourError } msg := fmt.Sprintf(colour+ - "%v %v%v%v| Price: $%v\tAmount: %v\tFee: $%v\tTotal: $%v\tDirection %v", + "%v %v%v%v| Price: %v\tDirection %v\tOrder placed: Amount: %v\tFee: %v\tTotal: %v", currencyStatistic.Events[i].FillEvent.GetTime().Format(gctcommon.SimpleTimeFormat), fSIL(exch, limit12), fSIL(a.String(), limit10), fSIL(currencyStatistic.Events[i].FillEvent.Pair().String(), limit14), currencyStatistic.Events[i].FillEvent.GetPurchasePrice().Round(8), + currencyStatistic.Events[i].FillEvent.GetDirection(), currencyStatistic.Events[i].FillEvent.GetAmount().Round(8), - currencyStatistic.Events[i].FillEvent.GetExchangeFee().Round(8), - currencyStatistic.Events[i].FillEvent.GetTotal().Round(8), - currencyStatistic.Events[i].FillEvent.GetDirection()) + currencyStatistic.Events[i].FillEvent.GetOrder().Fee, + currencyStatistic.Events[i].FillEvent.GetTotal().Round(8)) msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) @@ -187,14 +187,15 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. log.Infof(common.CurrencyStatistics, "%s Lowest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestUnrealisedPNL.Value, 8, ".", ","), c.LowestUnrealisedPNL.Time) log.Infof(common.CurrencyStatistics, "%s Highest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestRealisedPNL.Value, 8, ".", ","), c.HighestRealisedPNL.Time) log.Infof(common.CurrencyStatistics, "%s Lowest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestRealisedPNL.Value, 8, ".", ","), c.LowestRealisedPNL.Time) + log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) } else { log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) log.Infof(common.CurrencyStatistics, "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) - log.Infof(common.CurrencyStatistics, "%s Buy value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtValue, 8, ".", ","), last.Holdings.Timestamp) log.Infof(common.CurrencyStatistics, "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) + log.Infof(common.CurrencyStatistics, "%s Bought amount valued at last candle: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.ScaledBoughtValue, 8, ".", ",")) log.Infof(common.CurrencyStatistics, "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) - log.Infof(common.CurrencyStatistics, "%s Sell value: %s at %v", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldValue, 8, ".", ","), last.Holdings.Timestamp) log.Infof(common.CurrencyStatistics, "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Sold amount valued at last candle: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.ScaledSoldValue, 8, ".", ",")) } log.Infof(common.CurrencyStatistics, "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) @@ -348,11 +349,6 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { log.Infof(common.FundingStatistics, "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) log.Infof(common.FundingStatistics, "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) log.Infof(common.FundingStatistics, "%s Did strategy beat the benchmark: %v", sep, f.TotalUSDStatistics.DidStrategyBeatTheMarket) - log.Infof(common.FundingStatistics, "%s Buy Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.BuyOrders, ",")) - log.Infof(common.FundingStatistics, "%s Sell Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.SellOrders, ",")) - log.Infof(common.FundingStatistics, "%s Long Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.LongOrders, ",")) - log.Infof(common.FundingStatistics, "%s Short Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.ShortOrders, ",")) - log.Infof(common.FundingStatistics, "%s Total Orders: %s", sep, convert.IntToHumanFriendlyString(f.TotalUSDStatistics.TotalOrders, ",")) log.Infof(common.FundingStatistics, "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time) log.Infof(common.FundingStatistics, "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time) diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 3217a676196..23bc6dcd150 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -199,12 +199,12 @@ func (s *Statistic) CalculateAllResults() error { if err != nil { log.Error(common.Statistics, err) } - stats.PrintResults(exchangeName, assetItem, pair, s.FundManager.IsUsingExchangeLevelFunding()) stats.FinalHoldings = last.Holdings stats.InitialHoldings = stats.Events[0].Holdings stats.FinalOrders = last.Transactions s.StartDate = stats.Events[0].Time s.EndDate = last.Time + stats.PrintResults(exchangeName, assetItem, pair, s.FundManager.IsUsingExchangeLevelFunding()) finalResults = append(finalResults, FinalResultsHolder{ Exchange: exchangeName, diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index d6334897a03..737c41aadb2 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -297,9 +297,9 @@ func TestAddHoldingsForTime(t *testing.T) { BaseSize: eleet, BaseValue: eleet, SoldAmount: eleet, - SoldValue: eleet, + ScaledSoldValue: eleet, BoughtAmount: eleet, - BoughtValue: eleet, + ScaledBoughtValue: eleet, QuoteSize: eleet, TotalValueDifference: eleet, ChangeInTotalValuePercent: eleet, diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index dc9495dfdab..0181c6d3326 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -258,11 +258,6 @@ type TotalFundingStatistics struct { StrategyMovement decimal.Decimal RiskFreeRate decimal.Decimal CompoundAnnualGrowthRate decimal.Decimal - BuyOrders int64 - SellOrders int64 - LongOrders int64 - ShortOrders int64 - TotalOrders int64 MaxDrawdown Swing GeometricRatios *Ratios ArithmeticRatios *Ratios diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index ad6b3390bd7..caf59dfc108 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -434,12 +434,12 @@ {{.ExchangeName}} {{.Asset}} {{.Base}}-{{.Quote}} - {{ $.Prettify.Decimal64 .BuySide.MinimumSize}} {{.Base}} - {{ $.Prettify.Decimal64 .BuySide.MaximumSize}} {{.Base}} - {{ $.Prettify.Decimal64 .BuySide.MaximumTotal}} {{.Quote}} - {{ $.Prettify.Decimal64 .SellSide.MinimumSize}} {{.Base}} - {{ $.Prettify.Decimal64 .SellSide.MaximumSize}} {{.Base}} - {{ $.Prettify.Decimal64 .SellSide.MaximumTotal}} {{.Quote}} + {{ $.Prettify.Decimal64 .BuySide.MinimumSize}} {{if .Asset.IsFutures}} {{.Base}}-{{.Quote}} {{else}}{{.Base}} {{end}} + {{ $.Prettify.Decimal64 .BuySide.MaximumSize}} {{if .Asset.IsFutures}} {{.Base}}-{{.Quote}} {{else}}{{.Base}} {{end}} + {{ $.Prettify.Decimal64 .BuySide.MaximumTotal}} {{if .Asset.IsFutures}}{{else}}{{.Quote}} {{end}} + {{ $.Prettify.Decimal64 .SellSide.MinimumSize}} {{if .Asset.IsFutures}} {{.Base}}-{{.Quote}} {{else}}{{.Base}} {{end}} + {{ $.Prettify.Decimal64 .SellSide.MaximumSize}} {{if .Asset.IsFutures}} {{.Base}}-{{.Quote}} {{else}}{{.Base}} {{end}} + {{ $.Prettify.Decimal64 .SellSide.MaximumTotal}} {{if .Asset.IsFutures}}{{else}}{{.Quote}} {{end}} {{ $.Prettify.Decimal64 .MinimumSlippagePercent}}% {{ $.Prettify.Decimal64 .MaximumSlippagePercent}}% {{.TakerFee}} @@ -1271,7 +1271,7 @@ Buy Value - {{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtValue}} {{$val.FinalHoldings.Pair.Quote}} + {{ $.Prettify.Decimal8 $val.FinalHoldings.ScaledBoughtValue}} {{$val.FinalHoldings.Pair.Quote}} Buy Amount @@ -1283,7 +1283,7 @@ Sell Value - {{ $.Prettify.Decimal8 $val.FinalHoldings.SoldValue}} {{$val.FinalHoldings.Pair.Quote}} + {{ $.Prettify.Decimal8 $val.FinalHoldings.ScaledSoldValue}} {{$val.FinalHoldings.Pair.Quote}} Sell Amount @@ -1341,26 +1341,6 @@ {{ .DoesPerformanceBeatTheMarket }} {{ end }} - - Total Value Lost to Volume Sizing - {{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToVolumeSizing}} - - - Total Value Lost to Slippage - {{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLostToSlippage}} - - - Total Value Lost - {{ $.Prettify.Decimal8 $val.FinalHoldings.TotalValueLost}} - - - Total Fees - {{ $.Prettify.Decimal8 $val.FinalHoldings.TotalFees}} - - - Final Holdings - {{ $.Prettify.Decimal8 $val.FinalHoldings.BaseSize}} {{$val.FinalHoldings.Pair.Base}}-{{ $val.FinalHoldings.Pair.Quote }} - {{ if eq $.Statistics.FundingStatistics.Report.UsingExchangeLevelFunding false }} Final Holdings Value @@ -1617,26 +1597,6 @@ Lowest Holdings ${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.LowestHoldingValue.Value}} at {{.Statistics.FundingStatistics.TotalUSDStatistics.LowestHoldingValue.Time}} - - Total Buy Orders - {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.BuyOrders }} - - - Total Sell Orders - {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.SellOrders }} - - - Total Long Orders - {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.LongOrders }} - - - Total Short Orders - {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.ShortOrders }} - - - Total Orders - {{$.Prettify.Int .Statistics.FundingStatistics.TotalUSDStatistics.TotalOrders }} - Max Drawdown Start: {{ .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.Highest.Time }} End: {{ .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.Lowest.Time }} Drop: {{ $.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.MaxDrawdown.DrawdownPercent}}% @@ -1765,7 +1725,7 @@ {{ else }} {{$pair.Base}} Funds {{$pair.Quote}} Funds - Total Value + Total value in {{$pair.Quote}} {{ end }} From 9a19d9d007de53f93b027f24fe3ad60af3f8862f Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 5 May 2022 17:17:23 +1000 Subject: [PATCH 140/171] minifix --- backtester/eventhandlers/statistics/printresults.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 544bd85d3a4..c7daa75d5ea 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -187,9 +187,7 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. log.Infof(common.CurrencyStatistics, "%s Lowest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestUnrealisedPNL.Value, 8, ".", ","), c.LowestUnrealisedPNL.Time) log.Infof(common.CurrencyStatistics, "%s Highest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestRealisedPNL.Value, 8, ".", ","), c.HighestRealisedPNL.Time) log.Infof(common.CurrencyStatistics, "%s Lowest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestRealisedPNL.Value, 8, ".", ","), c.LowestRealisedPNL.Time) - log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) } else { - log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) log.Infof(common.CurrencyStatistics, "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) log.Infof(common.CurrencyStatistics, "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) log.Infof(common.CurrencyStatistics, "%s Bought amount valued at last candle: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.ScaledBoughtValue, 8, ".", ",")) @@ -197,6 +195,8 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. log.Infof(common.CurrencyStatistics, "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) log.Infof(common.CurrencyStatistics, "%s Sold amount valued at last candle: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.ScaledSoldValue, 8, ".", ",")) } + + log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) log.Infof(common.CurrencyStatistics, "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) log.Info(common.CurrencyStatistics, common.ColourH2+"------------------Max Drawdown-------------------------------"+common.ColourDefault) From bd33b0c68756916d71030eabbefa3ea7b3e9f6ba Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 6 May 2022 13:01:06 +1000 Subject: [PATCH 141/171] Fixes tests and fee bug --- backtester/engine/setup.go | 1 - .../portfolio/holdings/holdings_test.go | 17 +++++--- .../eventhandlers/portfolio/portfolio.go | 1 - .../statistics/currencystatistics_test.go | 42 +++++++++++++++---- .../eventhandlers/statistics/printresults.go | 2 +- cmd/exchange_wrapper_issues/main.go | 27 ++++++------ 6 files changed, 60 insertions(+), 30 deletions(-) diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index acf32d11046..4c019558939 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -467,7 +467,6 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange makerFee = apiMakerFee cfg.CurrencySettings[i].MakerFee = &makerFee cfg.CurrencySettings[i].UsingExchangeMakerFee = true - } if cfg.CurrencySettings[i].TakerFee == nil { takerFee = apiTakerFee diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index b19a6f9b251..8236c1fbd08 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -85,12 +85,14 @@ func TestUpdate(t *testing.T) { t.Error(err) } t1 := h.Timestamp // nolint:ifshort,nolintlint // false positive and triggers only on Windows - h.Update(&fill.Fill{ + err = h.Update(&fill.Fill{ Base: event.Base{ Time: time.Now(), }, }, pair(t)) - + if err != nil { + t.Error(err) + } if t1.Equal(h.Timestamp) { t.Errorf("expected '%v' received '%v'", h.Timestamp, t1) } @@ -134,7 +136,7 @@ func TestUpdateBuyStats(t *testing.T) { t.Error(err) } - h.update(&fill.Fill{ + err = h.update(&fill.Fill{ Base: event.Base{ Exchange: testExchange, Time: time.Now(), @@ -192,7 +194,7 @@ func TestUpdateBuyStats(t *testing.T) { t.Errorf("expected '%v' received '%v'", 1, h.TotalFees) } - h.update(&fill.Fill{ + err = h.update(&fill.Fill{ Base: event.Base{ Exchange: testExchange, Time: time.Now(), @@ -261,7 +263,7 @@ func TestUpdateSellStats(t *testing.T) { if err != nil { t.Error(err) } - h.update(&fill.Fill{ + err = h.update(&fill.Fill{ Base: event.Base{ Exchange: testExchange, Time: time.Now(), @@ -321,7 +323,7 @@ func TestUpdateSellStats(t *testing.T) { t.Errorf("expected '%v' received '%v'", 1, h.TotalFees) } - h.update(&fill.Fill{ + err = h.update(&fill.Fill{ Base: event.Base{ Exchange: testExchange, Time: time.Now(), @@ -351,6 +353,9 @@ func TestUpdateSellStats(t *testing.T) { Fee: 1, }, }, p) + if err != nil { + t.Error(err) + } if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 73a12167419..1dd4cc29b25 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -281,7 +281,6 @@ func (p *Portfolio) OnFill(ev fill.Event, funds funding.IFundReleaser) (fill.Eve if err != nil { log.Error(common.Portfolio, err) } - ev.SetExchangeFee(decimal.Zero) return ev, nil } diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index e5b756a6580..1796eaddc14 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -1,6 +1,7 @@ package statistics import ( + "errors" "testing" "time" @@ -20,11 +21,13 @@ import ( func TestCalculateResults(t *testing.T) { t.Parallel() - cs := CurrencyPairStatistic{} + a := asset.Spot + cs := CurrencyPairStatistic{ + Asset: a, + } tt1 := time.Now() tt2 := time.Now().Add(gctkline.OneDay.Duration()) exch := testExchange - a := asset.Spot p := currency.NewPair(currency.BTC, currency.USDT) even := event.Base{ Exchange: exch, @@ -251,8 +254,13 @@ func TestPrintResults(t *testing.T) { func TestCalculateHighestCommittedFunds(t *testing.T) { t.Parallel() - c := CurrencyPairStatistic{} - c.calculateHighestCommittedFunds() + c := CurrencyPairStatistic{ + Asset: asset.Spot, + } + err := c.calculateHighestCommittedFunds() + if !errors.Is(err, nil) { + t.Error(err) + } if !c.HighestCommittedFunds.Time.IsZero() { t.Error("expected no time with not committed funds") } @@ -260,14 +268,30 @@ func TestCalculateHighestCommittedFunds(t *testing.T) { tt2 := time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC) tt3 := time.Date(2021, 3, 1, 0, 0, 0, 0, time.UTC) c.Events = append(c.Events, - DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1337)}, Holdings: holdings.Holding{Timestamp: tt1, BaseSize: decimal.NewFromInt(10)}}, - DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1338)}, Holdings: holdings.Holding{Timestamp: tt2, BaseSize: decimal.NewFromInt(1337)}}, - DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1339)}, Holdings: holdings.Holding{Timestamp: tt3, BaseSize: decimal.NewFromInt(11)}}, + DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1337)}, Time: tt1, Holdings: holdings.Holding{Timestamp: tt1, CommittedFunds: decimal.NewFromInt(10), BaseSize: decimal.NewFromInt(10)}}, + DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1338)}, Time: tt2, Holdings: holdings.Holding{Timestamp: tt2, CommittedFunds: decimal.NewFromInt(1337), BaseSize: decimal.NewFromInt(1337)}}, + DataAtOffset{DataEvent: &kline.Kline{Close: decimal.NewFromInt(1339)}, Time: tt3, Holdings: holdings.Holding{Timestamp: tt3, CommittedFunds: decimal.NewFromInt(11), BaseSize: decimal.NewFromInt(11)}}, ) - c.calculateHighestCommittedFunds() - if c.HighestCommittedFunds.Time != tt1 { + err = c.calculateHighestCommittedFunds() + if !errors.Is(err, nil) { + t.Error(err) + } + if c.HighestCommittedFunds.Time != tt2 { t.Errorf("expected %v, received %v", tt2, c.HighestCommittedFunds.Time) } + + c.Asset = asset.Futures + c.HighestCommittedFunds = ValueAtTime{} + err = c.calculateHighestCommittedFunds() + if !errors.Is(err, nil) { + t.Error(err) + } + + c.Asset = asset.Binary + err = c.calculateHighestCommittedFunds() + if !errors.Is(err, asset.ErrNotSupported) { + t.Error(err) + } } func TestAnalysePNLGrowth(t *testing.T) { diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index c7daa75d5ea..32e92c672dd 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -111,7 +111,7 @@ func (s *Statistic) PrintAllEventsChronologically() { currencyStatistic.Events[i].FillEvent.GetPurchasePrice().Round(8), currencyStatistic.Events[i].FillEvent.GetDirection(), currencyStatistic.Events[i].FillEvent.GetAmount().Round(8), - currencyStatistic.Events[i].FillEvent.GetOrder().Fee, + currencyStatistic.Events[i].FillEvent.GetExchangeFee(), currencyStatistic.Events[i].FillEvent.GetTotal().Round(8)) msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) msg += common.ColourDefault diff --git a/cmd/exchange_wrapper_issues/main.go b/cmd/exchange_wrapper_issues/main.go index ab1e50899c0..0784364192c 100644 --- a/cmd/exchange_wrapper_issues/main.go +++ b/cmd/exchange_wrapper_issues/main.go @@ -565,12 +565,13 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) }) modifyRequest := order.Modify{ - ID: config.OrderSubmission.OrderID, - Type: testOrderType, - Side: testOrderSide, - Pair: p, - Price: config.OrderSubmission.Price, - Amount: config.OrderSubmission.Amount, + ID: config.OrderSubmission.OrderID, + Type: testOrderType, + Side: testOrderSide, + Pair: p, + Price: config.OrderSubmission.Price, + Amount: config.OrderSubmission.Amount, + AssetType: assetTypes[i], } modifyOrderResponse, err := e.ModifyOrder(context.TODO(), &modifyRequest) msg = "" @@ -655,9 +656,10 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) }) historyRequest := order.GetOrdersRequest{ - Type: testOrderType, - Side: testOrderSide, - Pairs: []currency.Pair{p}, + Type: testOrderType, + Side: testOrderSide, + Pairs: []currency.Pair{p}, + AssetType: assetTypes[i], } var getOrderHistoryResponse []order.Detail getOrderHistoryResponse, err = e.GetOrderHistory(context.TODO(), &historyRequest) @@ -674,9 +676,10 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) }) orderRequest := order.GetOrdersRequest{ - Type: testOrderType, - Side: testOrderSide, - Pairs: []currency.Pair{p}, + Type: testOrderType, + Side: testOrderSide, + Pairs: []currency.Pair{p}, + AssetType: assetTypes[i], } var getActiveOrdersResponse []order.Detail getActiveOrdersResponse, err = e.GetActiveOrders(context.TODO(), &orderRequest) From c89926bd77291e0723ecb27d53674a3f1952896c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 6 May 2022 14:04:21 +1000 Subject: [PATCH 142/171] Merge fixeroos --- backtester/common/common_types.go | 33 +------------------ backtester/engine/backtest_test.go | 4 +-- backtester/eventhandlers/exchange/exchange.go | 24 +++++++------- .../eventhandlers/portfolio/portfolio.go | 32 +++++++++--------- .../eventhandlers/portfolio/portfolio_test.go | 20 +++++------ .../eventhandlers/portfolio/size/size.go | 2 +- .../eventhandlers/portfolio/size/size_test.go | 2 +- .../eventhandlers/statistics/printresults.go | 15 +++++---- .../eventhandlers/statistics/statistics.go | 1 - .../ftxcashandcarry/ftxcashandcarry.go | 10 +++--- .../ftxcashandcarry/ftxcashandcarry_test.go | 14 ++++---- backtester/funding/collateralpair.go | 3 +- backtester/funding/collateralpair_test.go | 5 ++- backtester/funding/spotpair.go | 9 +++-- backtester/funding/spotpair_test.go | 9 +++-- backtester/report/report.go | 3 +- currency/code.go | 2 ++ exchanges/order/futures.go | 6 ++-- exchanges/order/futures_test.go | 4 +-- exchanges/order/order_types.go | 10 ++++-- exchanges/order/orders.go | 10 ++++++ 21 files changed, 100 insertions(+), 118 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 2075d38043e..a07ef61b4b3 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -17,39 +17,8 @@ const ( CandleStr = "candle" // TradeStr is a config readable data type to tell the backtester to retrieve trade data TradeStr = "trade" -) -// custom order side declarations for backtesting processing and -// decision-making -const ( - // DoNothing is an explicit signal for the backtester to not perform an action - // based upon indicator results - DoNothing order.Side = "DO NOTHING" - // TransferredFunds is a status signal to do nothing - TransferredFunds order.Side = "TRANSFERRED FUNDS" - // CouldNotBuy is flagged when a BUY signal is raised in the strategy/signal phase, but the - // portfolio manager or exchange cannot place an order - CouldNotBuy order.Side = "COULD NOT BUY" - // CouldNotSell is flagged when a SELL signal is raised in the strategy/signal phase, but the - // portfolio manager or exchange cannot place an order - CouldNotSell order.Side = "COULD NOT SELL" - CouldNotShort order.Side = "COULD NOT SHORT" - CouldNotLong order.Side = "COULD NOT LONG" - // ClosePosition is used to signal a complete closure - // of any exposure of a position - // This will handle any amount of exposure, no need to calculate how - // much to close - ClosePosition order.Side = "CLOSE POSITION" - CouldNotCloseShort order.Side = "COULD NOT CLOSE SHORT" - CouldNotCloseLong order.Side = "COULD NOT CLOSE LONG" - Liquidated order.Side = "LIQUIDATED" - // MissingData is signalled during the strategy/signal phase when data has been identified as missing - // No buy or sell events can occur - MissingData order.Side = "MISSING DATA" -) - -// DataCandle is an int64 representation of a candle data type -const ( + // DataCandle is an int64 representation of a candle data type DataCandle = iota DataTrade ) diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 58eeba90dd6..46eefcfe8e5 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -66,7 +66,7 @@ func (p portfolioOverride) CreateLiquidationOrdersForExchange(ev common.DataEven Reason: ev.GetReason(), }, ID: "1", - Direction: common.Liquidated, + Direction: gctorder.Short, // ?????????????????? }, }, nil } @@ -781,7 +781,7 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { if !ok { t.Fatal("expected order event") } - if ev2o.GetDirection() != common.Liquidated { + if ev2o.GetDirection() != gctorder.Short { t.Error("expected liquidation order") } } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 6c6fae19ebb..164976e8ff2 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -49,7 +49,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * FillDependentEvent: o.GetFillDependentEvent(), Liquidated: o.IsLiquidating(), } - if o.GetDirection() == common.DoNothing { + if o.GetDirection() == gctorder.DoNothing { return f, ErrDoNothing } if o.GetAssetType().IsFutures() && !o.IsClosingPosition() { @@ -122,9 +122,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * case gctorder.Sell, gctorder.Ask: f.SetDirection(gctorder.CouldNotSell) case gctorder.Short: - f.SetDirection(common.CouldNotShort) + f.SetDirection(gctorder.CouldNotShort) case gctorder.Long: - f.SetDirection(common.CouldNotLong) + f.SetDirection(gctorder.CouldNotLong) default: f.SetDirection(gctorder.DoNothing) } @@ -214,9 +214,9 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro } switch f.GetDirection() { case gctorder.Buy, gctorder.Bid: - f.SetDirection(common.CouldNotBuy) - case gctorder.Sell, gctorder.Ask, common.ClosePosition: - f.SetDirection(common.CouldNotSell) + f.SetDirection(gctorder.CouldNotBuy) + case gctorder.Sell, gctorder.Ask, gctorder.ClosePosition: + f.SetDirection(gctorder.CouldNotSell) } return orderError } @@ -230,7 +230,7 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro if err != nil { return err } - case gctorder.Sell, gctorder.Ask, common.ClosePosition: + case gctorder.Sell, gctorder.Ask, gctorder.ClosePosition: err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) if err != nil { return err @@ -254,9 +254,9 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro } switch f.GetDirection() { case gctorder.Short: - f.SetDirection(common.CouldNotShort) + f.SetDirection(gctorder.CouldNotShort) case gctorder.Long: - f.SetDirection(common.CouldNotLong) + f.SetDirection(gctorder.CouldNotLong) default: return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.GetDirection()) } @@ -288,11 +288,11 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c direction = gctorder.CouldNotSell case gctorder.Long: minMax = cs.BuySide - direction = common.CouldNotLong + direction = gctorder.CouldNotLong case gctorder.Short: minMax = cs.SellSide - direction = common.CouldNotShort - case common.ClosePosition: + direction = gctorder.CouldNotShort + case gctorder.ClosePosition: return nil default: direction = f.GetDirection() diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 8784a6fb98d..a11f04dec43 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -84,7 +84,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi var sizingFunds decimal.Decimal var side = ev.GetDirection() if ev.GetAssetType() == asset.Spot { - if side == common.ClosePosition { + if side == gctorder.ClosePosition { side = gctorder.Sell } pReader, err := funds.GetPairReader() @@ -98,7 +98,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi sizingFunds = pReader.BaseAvailable() } } else if ev.GetAssetType().IsFutures() { - if ev.GetDirection() == common.ClosePosition { + if ev.GetDirection() == gctorder.ClosePosition { // lookup position positions := lookup.FuturesTracker.GetPositions() if len(positions) == 0 { @@ -126,7 +126,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) sizedOrder.SetDirection(side) - if ev.GetDirection() == common.ClosePosition { + if ev.GetDirection() == gctorder.ClosePosition { sizedOrder.ClosingPosition = true } return p.evaluateOrder(ev, o, sizedOrder) @@ -142,16 +142,16 @@ func cannotPurchase(ev signal.Event, o *order.Order) (*order.Order, error) { o.AppendReason(notEnoughFundsTo + " " + ev.GetDirection().Lower()) switch ev.GetDirection() { case gctorder.Buy, gctorder.Bid: - o.SetDirection(common.CouldNotBuy) + o.SetDirection(gctorder.CouldNotBuy) case gctorder.Sell, gctorder.Ask: - o.SetDirection(common.CouldNotSell) + o.SetDirection(gctorder.CouldNotSell) case gctorder.Short: - o.SetDirection(common.CouldNotShort) + o.SetDirection(gctorder.CouldNotShort) case gctorder.Long: - o.SetDirection(common.CouldNotLong) + o.SetDirection(gctorder.CouldNotLong) default: // ensure that unknown scenarios don't affect anything - o.SetDirection(common.DoNothing) + o.SetDirection(gctorder.DoNothing) } ev.SetDirection(o.Direction) return o, nil @@ -168,14 +168,14 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, ev if err != nil { originalOrderSignal.AppendReason(err.Error()) switch d.GetDirection() { - case gctorder.Buy, common.CouldNotBuy: + case gctorder.Buy, gctorder.CouldNotBuy: originalOrderSignal.Direction = gctorder.CouldNotBuy - case gctorder.Sell, common.CouldNotSell: - originalOrderSignal.Direction = common.CouldNotSell + case gctorder.Sell, gctorder.CouldNotSell: + originalOrderSignal.Direction = gctorder.CouldNotSell case gctorder.Short: - originalOrderSignal.Direction = common.CouldNotShort + originalOrderSignal.Direction = gctorder.CouldNotShort case gctorder.Long: - originalOrderSignal.Direction = common.CouldNotLong + originalOrderSignal.Direction = gctorder.CouldNotLong default: originalOrderSignal.Direction = gctorder.DoNothing } @@ -195,9 +195,9 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi case gctorder.Sell, gctorder.Ask: originalOrderSignal.Direction = gctorder.CouldNotSell case gctorder.Long: - originalOrderSignal.Direction = common.CouldNotLong + originalOrderSignal.Direction = gctorder.CouldNotLong case gctorder.Short: - originalOrderSignal.Direction = common.CouldNotShort + originalOrderSignal.Direction = gctorder.CouldNotShort default: originalOrderSignal.Direction = gctorder.DoNothing } @@ -218,7 +218,7 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi case gctorder.Short, gctorder.Long: err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) - case common.ClosePosition: + case gctorder.ClosePosition: if originalOrderSignal.AssetType.IsFutures() { err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 60b991170fe..37e2b4a183c 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -1261,8 +1261,8 @@ func TestCannotPurchase(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - if result.Direction != common.CouldNotBuy { - t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotBuy) + if result.Direction != gctorder.CouldNotBuy { + t.Errorf("received '%v' expected '%v'", result.Direction, gctorder.CouldNotBuy) } s.Direction = gctorder.Sell @@ -1271,8 +1271,8 @@ func TestCannotPurchase(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - if result.Direction != common.CouldNotSell { - t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotSell) + if result.Direction != gctorder.CouldNotSell { + t.Errorf("received '%v' expected '%v'", result.Direction, gctorder.CouldNotSell) } s.Direction = gctorder.Short @@ -1281,8 +1281,8 @@ func TestCannotPurchase(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - if result.Direction != common.CouldNotShort { - t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotShort) + if result.Direction != gctorder.CouldNotShort { + t.Errorf("received '%v' expected '%v'", result.Direction, gctorder.CouldNotShort) } s.Direction = gctorder.Long @@ -1291,8 +1291,8 @@ func TestCannotPurchase(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - if result.Direction != common.CouldNotLong { - t.Errorf("received '%v' expected '%v'", result.Direction, common.CouldNotLong) + if result.Direction != gctorder.CouldNotLong { + t.Errorf("received '%v' expected '%v'", result.Direction, gctorder.CouldNotLong) } s.Direction = gctorder.UnknownSide @@ -1301,8 +1301,8 @@ func TestCannotPurchase(t *testing.T) { if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - if result.Direction != common.DoNothing { - t.Errorf("received '%v' expected '%v'", result.Direction, common.DoNothing) + if result.Direction != gctorder.DoNothing { + t.Errorf("received '%v' expected '%v'", result.Direction, gctorder.DoNothing) } } diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index a14d5456fbd..a77d25789ef 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -65,7 +65,7 @@ func (s *Size) calculateAmount(direction gctorder.Side, closePrice, amountAvaila var amount decimal.Decimal var err error switch direction { - case common.ClosePosition: + case gctorder.ClosePosition: amount = amountAvailable case gctorder.Buy, gctorder.Long: // check size against currency specific settings diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index 80ca27ccf18..854c85de413 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -233,7 +233,7 @@ func TestSizeOrder(t *testing.T) { t.Error(err) } - o.Direction = common.ClosePosition + o.Direction = gctorder.ClosePosition _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { t.Error(err) diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 32e92c672dd..08144279abf 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -76,13 +77,13 @@ func (s *Statistic) PrintAllEventsChronologically() { switch { case currencyStatistic.Events[i].FillEvent != nil: direction := currencyStatistic.Events[i].FillEvent.GetDirection() - if direction == common.CouldNotBuy || - direction == common.CouldNotSell || - direction == common.MissingData || - direction == common.DoNothing || - direction == common.TransferredFunds || - direction == "" { - if direction == common.DoNothing { + if direction == order.CouldNotBuy || + direction == order.CouldNotSell || + direction == order.MissingData || + direction == order.DoNothing || + direction == order.TransferredFunds || + direction == order.UnknownSide { + if direction == order.DoNothing { colour = common.ColourDarkGrey } msg := fmt.Sprintf(colour+ diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 54e39bd0040..23bc6dcd150 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -14,7 +14,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 3d614b5c0d0..b90fd24bcae 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -78,8 +78,8 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra return nil, err } - spotSignal.SetDirection(common.DoNothing) - futuresSignal.SetDirection(common.DoNothing) + spotSignal.SetDirection(order.DoNothing) + futuresSignal.SetDirection(order.DoNothing) fp := v.futureSignal.Latest().GetClosePrice() sp := v.spotSignal.Latest().GetClosePrice() diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(decimal.NewFromInt(100)) @@ -135,14 +135,14 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS response = append(response, spotSignal) case pos[len(pos)-1].Status == order.Open && isLastEvent: - spotSignal.SetDirection(common.ClosePosition) + spotSignal.SetDirection(order.ClosePosition) spotSignal.AppendReason("Selling asset on last event") - futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.SetDirection(order.ClosePosition) futuresSignal.AppendReason("Closing position on last event") response = append(response, futuresSignal, spotSignal) case pos[len(pos)-1].Status == order.Open && diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): - futuresSignal.SetDirection(common.ClosePosition) + futuresSignal.SetDirection(order.ClosePosition) futuresSignal.AppendReasonf("Closing position. Threshold %v", s.closeShortDistancePercentage) response = append(response, spotSignal, futuresSignal) case pos[len(pos)-1].Status == order.Closed && diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 8b3af5fc39e..d5f9f7fe90d 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -216,8 +216,8 @@ func TestCreateSignals(t *testing.T) { caseTested := false for i := range resp { if resp[i].GetAssetType().IsFutures() { - if resp[i].GetDirection() != common.ClosePosition { - t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) + if resp[i].GetDirection() != gctorder.ClosePosition { + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), gctorder.ClosePosition) } caseTested = true } @@ -239,8 +239,8 @@ func TestCreateSignals(t *testing.T) { caseTested = false for i := range resp { if resp[i].GetAssetType().IsFutures() { - if resp[i].GetDirection() != common.ClosePosition { - t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) + if resp[i].GetDirection() != gctorder.ClosePosition { + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), gctorder.ClosePosition) } caseTested = true } @@ -263,7 +263,7 @@ func TestCreateSignals(t *testing.T) { for i := range resp { if resp[i].GetAssetType().IsFutures() { if resp[i].GetDirection() != gctorder.Short { - t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), common.ClosePosition) + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), gctorder.Short) } caseTested = true } @@ -411,7 +411,7 @@ func TestOnSimultaneousSignals(t *testing.T) { if len(resp) != 2 { t.Fatalf("received '%v' expected '%v", len(resp), 2) } - if resp[0].GetDirection() != common.DoNothing { - t.Errorf("received '%v' expected '%v", resp[0].GetDirection(), common.DoNothing) + if resp[0].GetDirection() != gctorder.DoNothing { + t.Errorf("received '%v' expected '%v", resp[0].GetDirection(), gctorder.DoNothing) } } diff --git a/backtester/funding/collateralpair.go b/backtester/funding/collateralpair.go index b747603d54f..6b60881e55f 100644 --- a/backtester/funding/collateralpair.go +++ b/backtester/funding/collateralpair.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/currency" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -86,7 +85,7 @@ func (c *CollateralPair) Reserve(amount decimal.Decimal, side gctorder.Side) err switch side { case gctorder.Long, gctorder.Short: return c.collateral.Reserve(amount) - case common.ClosePosition: + case gctorder.ClosePosition: return c.collateral.Release(amount, amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", diff --git a/backtester/funding/collateralpair_test.go b/backtester/funding/collateralpair_test.go index b8c5f852fba..a61b70b9c2a 100644 --- a/backtester/funding/collateralpair_test.go +++ b/backtester/funding/collateralpair_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -16,7 +15,7 @@ func TestCollateralCanPlaceOrder(t *testing.T) { c := &CollateralPair{ collateral: &Item{available: decimal.NewFromInt(1337)}, } - if !c.CanPlaceOrder("") { + if !c.CanPlaceOrder(gctorder.UnknownSide) { t.Error("expected true") } } @@ -262,7 +261,7 @@ func TestCollateralReserve(t *testing.T) { t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1335)) } - err = c.Reserve(decimal.NewFromInt(2), common.ClosePosition) + err = c.Reserve(decimal.NewFromInt(2), gctorder.ClosePosition) if !errors.Is(err, expectedError) { t.Errorf("recevied '%v' expected '%v'", err, expectedError) } diff --git a/backtester/funding/spotpair.go b/backtester/funding/spotpair.go index d5ff7d61e6e..559ac4484c1 100644 --- a/backtester/funding/spotpair.go +++ b/backtester/funding/spotpair.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -45,7 +44,7 @@ func (p *SpotPair) Reserve(amount decimal.Decimal, side order.Side) error { switch side { case order.Buy, order.Bid: return p.quote.Reserve(amount) - case order.Sell, order.Ask, common.ClosePosition: + case order.Sell, order.Ask, order.ClosePosition: return p.base.Reserve(amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", @@ -64,7 +63,7 @@ func (p *SpotPair) Release(amount, diff decimal.Decimal, side order.Side) error switch side { case order.Buy, order.Bid: return p.quote.Release(amount, diff) - case order.Sell, order.Ask, common.ClosePosition: + case order.Sell, order.Ask, order.ClosePosition: return p.base.Release(amount, diff) } return fmt.Errorf("%w for %v %v %v. Unknown side %v", @@ -81,7 +80,7 @@ func (p *SpotPair) IncreaseAvailable(amount decimal.Decimal, side order.Side) er switch side { case order.Buy, order.Bid: return p.base.IncreaseAvailable(amount) - case order.Sell, order.Ask, common.ClosePosition: + case order.Sell, order.Ask, order.ClosePosition: return p.quote.IncreaseAvailable(amount) } return fmt.Errorf("%w for %v %v %v. Unknown side %v", @@ -99,7 +98,7 @@ func (p *SpotPair) CanPlaceOrder(side order.Side) bool { switch side { case order.Buy, order.Bid: return p.quote.CanPlaceOrder() - case order.Sell, order.Ask, common.ClosePosition: + case order.Sell, order.Ask, order.ClosePosition: return p.base.CanPlaceOrder() } return false diff --git a/backtester/funding/spotpair_test.go b/backtester/funding/spotpair_test.go index 138625a6d11..f0de925c4b7 100644 --- a/backtester/funding/spotpair_test.go +++ b/backtester/funding/spotpair_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/shopspring/decimal" - "github.com/thrasher-corp/gocryptotrader/backtester/common" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -114,7 +113,7 @@ func TestReservePair(t *testing.T) { if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Reserve(elite, common.DoNothing) + err = pairItems.Reserve(elite, gctorder.DoNothing) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } @@ -163,7 +162,7 @@ func TestReleasePair(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } - err = pairItems.Release(elite, decimal.Zero, common.DoNothing) + err = pairItems.Release(elite, decimal.Zero, gctorder.DoNothing) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } @@ -221,7 +220,7 @@ func TestIncreaseAvailablePair(t *testing.T) { t.Errorf("received '%v' expected '%v'", elite, pairItems.base.available) } - err = pairItems.IncreaseAvailable(elite, common.DoNothing) + err = pairItems.IncreaseAvailable(elite, gctorder.DoNothing) if !errors.Is(err, errCannotAllocate) { t.Errorf("received '%v' expected '%v'", err, errCannotAllocate) } @@ -236,7 +235,7 @@ func TestCanPlaceOrderPair(t *testing.T) { base: &Item{}, quote: &Item{}, } - if p.CanPlaceOrder(common.DoNothing) { + if p.CanPlaceOrder(gctorder.DoNothing) { t.Error("expected false") } if p.CanPlaceOrder(gctorder.Buy) { diff --git a/backtester/report/report.go b/backtester/report/report.go index 48965262645..136705e9cbc 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -9,6 +9,7 @@ import ( "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" @@ -167,7 +168,7 @@ func (d *Data) enhanceCandles() error { } if !requiresIteration { if statsForCandles.Events[intVal].Time.Equal(d.OriginalCandles[intVal].Candles[j].Time) && - (statsForCandles.Events[intVal].SignalEvent == nil || statsForCandles.Events[intVal].SignalEvent.GetDirection() == common.MissingData) && + (statsForCandles.Events[intVal].SignalEvent == nil || statsForCandles.Events[intVal].SignalEvent.GetDirection() == order.MissingData) && len(enhancedKline.Candles) > 0 { enhancedCandle.copyCloseFromPreviousEvent(&enhancedKline) } diff --git a/currency/code.go b/currency/code.go index 6cc151faf9b..4f924fbe868 100644 --- a/currency/code.go +++ b/currency/code.go @@ -81,6 +81,8 @@ func (b *BaseCodes) HasData() bool { // GetFullCurrencyData returns a type that is read to dump to file func (b *BaseCodes) GetFullCurrencyData() (File, error) { var file File + b.mtx.Lock() + defer b.mtx.Unlock() for i := range b.Items { switch b.Items[i].Role { case Unset: diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index b72b1c930d5..9da635b1862 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -491,14 +491,14 @@ func (p *PositionTracker) Liquidate(price decimal.Decimal, t time.Time) error { return fmt.Errorf("%w cannot liquidate from a different time. PNL snapshot %v. Liquidation request on %v Status: %v", errCannotLiquidate, latest.Time, t, p.status) } p.status = Liquidated - p.currentDirection = SideNA + p.currentDirection = ClosePosition p.exposure = decimal.Zero p.realisedPNL = decimal.Zero p.unrealisedPNL = decimal.Zero _, err = upsertPNLEntry(p.pnlHistory, &PNLResult{ Time: t, Price: price, - Direction: SideNA, + Direction: ClosePosition, IsLiquidated: true, IsOrder: true, Status: p.status, @@ -679,7 +679,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { case shortSide.GreaterThan(longSide): p.currentDirection = Short default: - p.currentDirection = SideNA + p.currentDirection = ClosePosition } if p.currentDirection.IsLong() { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index b9b17e0aae0..5f0becf9ea5 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -178,7 +178,7 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, nil) { t.Error(err) } - if f.currentDirection != SideNA { + if f.currentDirection != ClosePosition { t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) } if f.status != Closed { @@ -189,7 +189,7 @@ func TestTrackNewOrder(t *testing.T) { if !errors.Is(err, ErrPositionClosed) { t.Error(err) } - if f.currentDirection != SideNA { + if f.currentDirection != ClosePosition { t.Errorf("expected recognition that its unknown, received '%v'", f.currentDirection) } if f.status != Closed { diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 850e9f72d63..b53edb4b23c 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -266,7 +266,7 @@ const ( Closed Pending Cancelling - Liquidated Status = "LIQUIDATED" + Liquidated ) // Type enforces a standard for order types across the code base @@ -293,7 +293,7 @@ const ( ) // Side enforces a standard for order sides across the code base -type Side uint16 +type Side uint32 // Order side types const ( @@ -305,13 +305,17 @@ const ( AnySide Long Short + ClosePosition // Backtester signal types DoNothing TransferredFunds CouldNotBuy CouldNotSell + CouldNotShort + CouldNotLong + CouldNotCloseShort + CouldNotCloseLong MissingData - SideNA Side = "N/A" ) // ByPrice used for sorting orders by price diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index ec7ede001c0..c7b7b071b83 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -541,6 +541,8 @@ func (s Side) String() string { return "SHORT" case AnySide: return "ANY" + case ClosePosition: + return "CLOSE POSITION" // Backtester signal types below. case DoNothing: return "DO NOTHING" @@ -550,6 +552,14 @@ func (s Side) String() string { return "COULD NOT BUY" case CouldNotSell: return "COULD NOT SELL" + case CouldNotShort: + return "COULD NOT SHORT" + case CouldNotLong: + return "COULD NOT LONG" + case CouldNotCloseShort: + return "COULD NOT CLOSE SHORT" + case CouldNotCloseLong: + return "COULD NOT CLOSE LONG" case MissingData: return "MISSING DATA" default: From b8e33e581721f7cc6090551b7c6c3755e10b641d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 6 May 2022 15:11:54 +1000 Subject: [PATCH 143/171] Microfixes --- engine/order_manager_test.go | 2 +- exchanges/order/orders.go | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index ea43663a0c2..b47562a64ce 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -876,7 +876,7 @@ func TestProcessOrders(t *testing.T) { t.Error(err) } if len(res) != 1 { - t.Errorf("Expected 3 result, got: %d", len(res)) + t.Errorf("Expected 1 result, got: %d", len(res)) } if res[0].Status != order.Active { t.Errorf("Order 1 should be active, but status is %s", res[0].Status) diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index c7b7b071b83..e74aaa505fb 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -18,7 +18,7 @@ const ( orderSubmissionValidSides = Buy | Sell | Bid | Ask | Long | Short shortSide = Short | Sell | Ask longSide = Long | Buy | Bid - inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | Expired | Closed | AnyStatus | Cancelling + inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | Expired | Closed | AnyStatus | Cancelling | Liquidated activeStatuses = Active | Open | PartiallyFilled | New | PendingCancel | Hidden | AutoDeleverage | Pending bypassSideFilter = UnknownSide | AnySide bypassTypeFilter = UnknownType | AnyType @@ -450,7 +450,13 @@ func (d *Detail) IsActive() bool { func (d *Detail) IsInactive() bool { return d.Amount <= 0 || d.Amount <= d.ExecutedAmount || - inactiveStatuses&d.Status == d.Status + d.Status.IsInactive() +} + +// IsInactive returns true if the status indicates it is +// currently not available on the exchange +func (s Status) IsInactive() bool { + return inactiveStatuses&s == s } // GenerateInternalOrderID sets a new V4 order ID or a V5 order ID if @@ -1057,10 +1063,3 @@ func (m *Modify) Validate(opt ...validate.Checker) error { } return nil } - -// IsInactive returns true if order is closed. Only for explicit closed status -// eg insufficient_balance is likely closed, but not concrete enough to include -func (s Status) IsInactive() bool { - return s == Filled || s == Cancelled || s == InsufficientBalance || s == MarketUnavailable || - s == Rejected || s == PartiallyCancelled || s == Expired || s == Closed || s == Liquidated -} From 2bdb280e60c4cc4be87ee9f23ef73ce51203723d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Tue, 10 May 2022 15:44:37 +1000 Subject: [PATCH 144/171] Updates orderPNL on first Correctly utilises fees. Adds committed funds --- backtester/config/config_test.go | 2 +- .../config/examples/ftx-cash-carry.strat | 2 + backtester/engine/backtest_test.go | 2 +- .../portfolio/holdings/holdings.go | 100 ++++++++++-------- .../eventhandlers/portfolio/portfolio.go | 14 +-- .../portfolio/portfolio_types.go | 2 +- .../eventhandlers/portfolio/size/size.go | 95 +++++++++-------- .../eventhandlers/statistics/statistics.go | 2 +- .../statistics/statistics_types.go | 2 +- backtester/funding/funding.go | 3 +- backtester/report/report_types.go | 4 +- backtester/report/tpl.gohtml | 2 + exchanges/order/futures.go | 31 ++++-- exchanges/order/futures_test.go | 34 ++++-- exchanges/order/futures_types.go | 1 + 15 files changed, 173 insertions(+), 123 deletions(-) diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 3d802ed7f75..4cee1fe60b2 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -25,7 +25,7 @@ const ( testExchange = "ftx" dca = "dollarcostaverage" // change this if you modify a config and want it to save to the example folder - saveConfig = false + saveConfig = !false ) var ( diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 4aac187c764..1dac4b22bfd 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -36,6 +36,7 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", + "maker-fee-override": "0.001", "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, @@ -60,6 +61,7 @@ "min-slippage-percent": "0", "max-slippage-percent": "0", "maker-fee-override": "0.001", + "taker-fee-override": "0.002", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 46eefcfe8e5..666195169ce 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -66,7 +66,7 @@ func (p portfolioOverride) CreateLiquidationOrdersForExchange(ev common.DataEven Reason: ev.GetReason(), }, ID: "1", - Direction: gctorder.Short, // ?????????????????? + Direction: gctorder.Short, }, }, nil } diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index baa4449d0cd..f7299cbbcea 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -9,6 +9,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" ) // Create makes a Holding struct to track total values of strategy holdings over the course of a backtesting run @@ -69,69 +70,74 @@ func (h *Holding) UpdateValue(d common.DataEventHandler) { h.Timestamp = d.GetTime() latest := d.GetClosePrice() h.Offset = d.GetOffset() - h.updateValue(latest) + h.scaleValuesToCurrentPrice(latest) } func (h *Holding) update(e fill.Event, f funding.IFundReader) error { direction := e.GetDirection() - if o := e.GetOrder(); o != nil { - amount := decimal.NewFromFloat(o.Amount) - fee := decimal.NewFromFloat(o.Fee) - price := decimal.NewFromFloat(o.Price) - a := e.GetAssetType() - switch { - case a == asset.Spot: - spotR, err := f.GetPairReader() - if err != nil { - return err - } - h.BaseSize = spotR.BaseAvailable() - h.QuoteSize = spotR.QuoteAvailable() - case a.IsFutures(): - collat, err := f.GetCollateralReader() - if err != nil { - return err - } - h.BaseSize = collat.CurrentHoldings() - h.QuoteSize = collat.AvailableFunds() - default: - return fmt.Errorf("%v %w", a, asset.ErrNotSupported) - } - - h.BaseValue = h.BaseSize.Mul(price) - h.TotalFees = h.TotalFees.Add(fee) - if e.GetAssetType().IsFutures() { - // responsibility of tracking futures orders is - // with order.PositionTracker - return nil + o := e.GetOrder() + if o == nil { + h.TotalValueLostToVolumeSizing = h.TotalValueLostToVolumeSizing.Add(e.GetClosePrice().Sub(e.GetVolumeAdjustedPrice()).Mul(e.GetAmount())) + h.TotalValueLostToSlippage = h.TotalValueLostToSlippage.Add(e.GetVolumeAdjustedPrice().Sub(e.GetPurchasePrice()).Mul(e.GetAmount())) + h.scaleValuesToCurrentPrice(e.GetClosePrice()) + return nil + } + amount := decimal.NewFromFloat(o.Amount) + fee := decimal.NewFromFloat(o.Fee) + price := decimal.NewFromFloat(o.Price) + a := e.GetAssetType() + switch { + case a == asset.Spot: + spotR, err := f.GetPairReader() + if err != nil { + return err } - switch direction { - case order.Buy, - order.Bid: - h.BoughtAmount = h.BoughtAmount.Add(amount) - h.ScaledBoughtValue = h.BoughtAmount.Mul(price) - h.CommittedFunds = h.CommittedFunds.Add(amount.Mul(price)) - case order.Sell, - order.Ask: - h.SoldAmount = h.SoldAmount.Add(amount) - h.ScaledSoldValue = h.SoldAmount.Mul(price) - h.CommittedFunds = h.CommittedFunds.Sub(amount.Mul(price)) + h.BaseSize = spotR.BaseAvailable() + h.QuoteSize = spotR.QuoteAvailable() + case a.IsFutures(): + collat, err := f.GetCollateralReader() + if err != nil { + return err } + h.BaseSize = collat.CurrentHoldings() + h.QuoteSize = collat.AvailableFunds() + default: + return fmt.Errorf("%v %w", a, asset.ErrNotSupported) + } + + h.BaseValue = h.BaseSize.Mul(price) + h.TotalFees = h.TotalFees.Add(fee) + if e.GetAssetType().IsFutures() { + // responsibility of tracking futures orders is + // with order.PositionTracker + return nil + } + switch direction { + case order.Buy, + order.Bid: + h.BoughtAmount = h.BoughtAmount.Add(amount) + h.ScaledBoughtValue = h.BoughtAmount.Mul(price) + h.CommittedFunds = h.BaseSize.Mul(price) + case order.Sell, + order.Ask: + h.SoldAmount = h.SoldAmount.Add(amount) + h.ScaledSoldValue = h.SoldAmount.Mul(price) + h.CommittedFunds = h.BaseSize.Mul(price) } h.TotalValueLostToVolumeSizing = h.TotalValueLostToVolumeSizing.Add(e.GetClosePrice().Sub(e.GetVolumeAdjustedPrice()).Mul(e.GetAmount())) h.TotalValueLostToSlippage = h.TotalValueLostToSlippage.Add(e.GetVolumeAdjustedPrice().Sub(e.GetPurchasePrice()).Mul(e.GetAmount())) - h.updateValue(e.GetClosePrice()) + h.scaleValuesToCurrentPrice(e.GetClosePrice()) return nil } -func (h *Holding) updateValue(latestPrice decimal.Decimal) { +func (h *Holding) scaleValuesToCurrentPrice(currentPrice decimal.Decimal) { origPosValue := h.BaseValue origBoughtValue := h.ScaledBoughtValue origSoldValue := h.ScaledSoldValue origTotalValue := h.TotalValue - h.BaseValue = h.BaseSize.Mul(latestPrice) - h.ScaledBoughtValue = h.BoughtAmount.Mul(latestPrice) - h.ScaledSoldValue = h.SoldAmount.Mul(latestPrice) + h.BaseValue = h.BaseSize.Mul(currentPrice) + h.ScaledBoughtValue = h.BoughtAmount.Mul(currentPrice) + h.ScaledSoldValue = h.SoldAmount.Mul(currentPrice) h.TotalValue = h.BaseValue.Add(h.QuoteSize) h.TotalValueDifference = h.TotalValue.Sub(origTotalValue) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index a11f04dec43..91815a00b57 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -187,7 +187,7 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, ev } func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, originalOrderSignal *order.Order, sizingFunds decimal.Decimal, funds funding.IFundReserver) *order.Order { - sizedOrder, err := p.sizeManager.SizeOrder(originalOrderSignal, sizingFunds, cs) + sizedOrder, estFee, err := p.sizeManager.SizeOrder(originalOrderSignal, sizingFunds, cs) if err != nil || sizedOrder.Amount.IsZero() { switch originalOrderSignal.Direction { case gctorder.Buy, gctorder.Bid: @@ -210,20 +210,20 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi } switch d.GetDirection() { case gctorder.Buy, gctorder.Bid: - err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice), gctorder.Buy) + err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee), gctorder.Buy) sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice) case gctorder.Sell, gctorder.Ask: - err = funds.Reserve(sizedOrder.Amount, gctorder.Sell) + err = funds.Reserve(sizedOrder.Amount.Add(estFee), gctorder.Sell) sizedOrder.AllocatedSize = sizedOrder.Amount case gctorder.Short, gctorder.Long: - err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) case gctorder.ClosePosition: if originalOrderSignal.AssetType.IsFutures() { - err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) } else { - err = funds.Reserve(sizedOrder.Amount, d.GetDirection()) + err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount } default: @@ -332,6 +332,7 @@ func (p *Portfolio) setHoldingsForOffset(h *holdings.Holding, overwriteExisting if lookup.HoldingsSnapshots[i].Offset == h.Offset { if overwriteExisting { lookup.HoldingsSnapshots[i] = *h + p.exchangeAssetPairSettings[h.Exchange][h.Asset][h.Pair] = lookup return nil } return errHoldingsAlreadySet @@ -342,6 +343,7 @@ func (p *Portfolio) setHoldingsForOffset(h *holdings.Holding, overwriteExisting } lookup.HoldingsSnapshots = append(lookup.HoldingsSnapshots, *h) + p.exchangeAssetPairSettings[h.Exchange][h.Asset][h.Pair] = lookup return nil } diff --git a/backtester/eventhandlers/portfolio/portfolio_types.go b/backtester/eventhandlers/portfolio/portfolio_types.go index fb1d169a755..6e6036d7e77 100644 --- a/backtester/eventhandlers/portfolio/portfolio_types.go +++ b/backtester/eventhandlers/portfolio/portfolio_types.go @@ -69,7 +69,7 @@ type Handler interface { // SizeHandler is the interface to help size orders type SizeHandler interface { - SizeOrder(order.Event, decimal.Decimal, *exchange.Settings) (*order.Order, error) + SizeOrder(order.Event, decimal.Decimal, *exchange.Settings) (*order.Order, decimal.Decimal, error) } // Settings holds all important information for the portfolio manager diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index a77d25789ef..5e88218108c 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -12,16 +12,16 @@ import ( ) // SizeOrder is responsible for ensuring that the order size is within config limits -func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exchange.Settings) (*order.Order, error) { +func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exchange.Settings) (*order.Order, decimal.Decimal, error) { if o == nil || cs == nil { - return nil, common.ErrNilArguments + return nil, decimal.Decimal{}, common.ErrNilArguments } if amountAvailable.LessThanOrEqual(decimal.Zero) { - return nil, errNoFunds + return nil, decimal.Decimal{}, errNoFunds } retOrder, ok := o.(*order.Order) if !ok { - return nil, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) + return nil, decimal.Decimal{}, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) } if fde := o.GetFillDependentEvent(); fde != nil && fde.MatchOrderAmount() { scalingInfo, err := cs.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ @@ -34,98 +34,103 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc FreeCollateral: amountAvailable, }) if err != nil { - return nil, err + return nil, decimal.Decimal{}, err } initialAmount := amountAvailable.Mul(scalingInfo.Weighting).Div(fde.GetClosePrice()) oNotionalPosition := initialAmount.Mul(o.GetClosePrice()) - sizedAmount, err := s.calculateAmount(o.GetDirection(), o.GetClosePrice(), oNotionalPosition, cs, o) + sizedAmount, estFee, err := s.calculateAmount(o.GetDirection(), o.GetClosePrice(), oNotionalPosition, cs, o) if err != nil { - return nil, err + return nil, decimal.Decimal{}, err } scaledCollateralFromAmount := sizedAmount.Mul(scalingInfo.Weighting) excess := amountAvailable.Sub(sizedAmount).Add(scaledCollateralFromAmount) if excess.IsNegative() { - return nil, fmt.Errorf("%w not enough funding for position", errCannotAllocate) + return nil, decimal.Decimal{}, fmt.Errorf("%w not enough funding for position", errCannotAllocate) } retOrder.SetAmount(sizedAmount) fde.SetAmount(sizedAmount) - return retOrder, nil + + return retOrder, estFee, nil } - amount, err := s.calculateAmount(retOrder.Direction, retOrder.ClosePrice, amountAvailable, cs, o) + amount, estFee, err := s.calculateAmount(retOrder.Direction, retOrder.ClosePrice, amountAvailable, cs, o) if err != nil { - return nil, err + return nil, decimal.Decimal{}, err } retOrder.SetAmount(amount) - return retOrder, nil + return retOrder, estFee, nil } -func (s *Size) calculateAmount(direction gctorder.Side, closePrice, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (decimal.Decimal, error) { - var amount decimal.Decimal +func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (decimal.Decimal, decimal.Decimal, error) { + var amount, fee, portfolioAmount, portfolioFee decimal.Decimal var err error switch direction { case gctorder.ClosePosition: amount = amountAvailable case gctorder.Buy, gctorder.Long: // check size against currency specific settings - amount, err = s.calculateBuySize(closePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) + amount, fee, err = s.calculateBuySize(price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) if err != nil { - return decimal.Decimal{}, err + return decimal.Decimal{}, decimal.Decimal{}, err } // check size against portfolio specific settings - var portfolioSize decimal.Decimal - portfolioSize, err = s.calculateBuySize(closePrice, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) + portfolioAmount, portfolioFee, err = s.calculateBuySize(price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) if err != nil { - return decimal.Decimal{}, err + return decimal.Decimal{}, decimal.Decimal{}, err } // global settings overrule individual currency settings - if amount.GreaterThan(portfolioSize) { - amount = portfolioSize + if amount.GreaterThan(portfolioAmount) { + amount = portfolioAmount + fee = portfolioFee } case gctorder.Sell, gctorder.Short: // check size against currency specific settings - amount, err = s.calculateSellSize(closePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) + amount, fee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) if err != nil { - return decimal.Decimal{}, err + return decimal.Decimal{}, decimal.Decimal{}, err } // check size against portfolio specific settings - portfolioSize, err := s.calculateSellSize(closePrice, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) + portfolioAmount, portfolioFee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) if err != nil { - return decimal.Decimal{}, err + return decimal.Decimal{}, decimal.Decimal{}, err } // global settings overrule individual currency settings - if amount.GreaterThan(portfolioSize) { - amount = portfolioSize + if amount.GreaterThan(portfolioAmount) { + amount = portfolioAmount + fee = portfolioFee } default: - return decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) + return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } if o.GetAmount().IsPositive() { - setAmountSize := o.GetAmount().Mul(closePrice) + setAmountSize := o.GetAmount().Mul(price) if setAmountSize.LessThan(amount) { amount = setAmountSize + fee = amount.Mul(price).Mul(cs.TakerFee) } } if amount.LessThanOrEqual(decimal.Zero) { - return decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) + return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } - return amount, nil + + return amount, fee, nil } // calculateBuySize respects config rules and calculates the amount of money // that is allowed to be spent/sold for an event. // As fee calculation occurs during the actual ordering process // this can only attempt to factor the potential fee to remain under the max rules -func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal.Decimal, minMaxSettings exchange.MinMax) (decimal.Decimal, error) { +func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal.Decimal, minMaxSettings exchange.MinMax) (decimal.Decimal, decimal.Decimal, error) { if availableFunds.LessThanOrEqual(decimal.Zero) { - return decimal.Zero, errNoFunds + return decimal.Decimal{}, decimal.Decimal{}, errNoFunds } if price.IsZero() { - return decimal.Zero, nil + return decimal.Decimal{}, decimal.Decimal{}, nil } - amount := availableFunds.Mul(decimal.NewFromInt(1).Sub(feeRate)).Div(price) + var amount, fee decimal.Decimal + amount = availableFunds.Mul(decimal.NewFromInt(1).Sub(feeRate)).Div(price) if !buyLimit.IsZero() && buyLimit.GreaterThanOrEqual(minMaxSettings.MinimumSize) && (buyLimit.LessThanOrEqual(minMaxSettings.MaximumSize) || minMaxSettings.MaximumSize.IsZero()) && @@ -139,9 +144,10 @@ func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal amount = minMaxSettings.MaximumTotal.Mul(decimal.NewFromInt(1).Sub(feeRate)).Div(price) } if amount.LessThan(minMaxSettings.MinimumSize) && minMaxSettings.MinimumSize.GreaterThan(decimal.Zero) { - return decimal.Zero, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize) + return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize) } - return amount, nil + fee = amount.Mul(price).Mul(feeRate) + return amount, fee, nil } // calculateSellSize respects config rules and calculates the amount of money @@ -150,15 +156,16 @@ func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal // eg BTC-USD baseAmount will be BTC to be sold // As fee calculation occurs during the actual ordering process // this can only attempt to factor the potential fee to remain under the max rules -func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.Decimal, minMaxSettings exchange.MinMax) (decimal.Decimal, error) { +func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.Decimal, minMaxSettings exchange.MinMax) (decimal.Decimal, decimal.Decimal, error) { if baseAmount.LessThanOrEqual(decimal.Zero) { - return decimal.Zero, errNoFunds + return decimal.Decimal{}, decimal.Decimal{}, errNoFunds } if price.IsZero() { - return decimal.Zero, nil + return decimal.Decimal{}, decimal.Decimal{}, nil } oneMFeeRate := decimal.NewFromInt(1).Sub(feeRate) - amount := baseAmount.Mul(oneMFeeRate) + var amount, fee decimal.Decimal + amount = baseAmount.Mul(oneMFeeRate) if !sellLimit.IsZero() && sellLimit.GreaterThanOrEqual(minMaxSettings.MinimumSize) && (sellLimit.LessThanOrEqual(minMaxSettings.MaximumSize) || minMaxSettings.MaximumSize.IsZero()) && @@ -172,8 +179,8 @@ func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.D amount = minMaxSettings.MaximumTotal.Mul(oneMFeeRate).Div(price) } if amount.LessThan(minMaxSettings.MinimumSize) && minMaxSettings.MinimumSize.GreaterThan(decimal.Zero) { - return decimal.Zero, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize) + return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w. Sized: '%v' Minimum: '%v'", errLessThanMinimum, amount, minMaxSettings.MinimumSize) } - - return amount, nil + fee = amount.Mul(price).Mul(feeRate) + return amount, fee, nil } diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index 23bc6dcd150..d2d7c1137e6 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -46,7 +46,7 @@ func (s *Statistic) SetupEventForTime(ev common.DataEventHandler) error { } } lookup.Events = append(lookup.Events, - DataAtOffset{ + &DataAtOffset{ DataEvent: ev, Offset: ev.GetOffset(), Time: ev.GetTime(), diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 0181c6d3326..f91d22343e3 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -170,7 +170,7 @@ type CurrencyPairStatistic struct { TotalValueLostToSlippage decimal.Decimal TotalValueLost decimal.Decimal - Events []DataAtOffset `json:"-"` + Events []*DataAtOffset `json:"-"` MaxDrawdown Swing `json:"max-drawdown,omitempty"` HighestCommittedFunds ValueAtTime `json:"highest-committed-funds"` diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 6f7c1191d1a..4c0bc207edb 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -117,8 +117,9 @@ func (f *FundManager) CreateSnapshot(t time.Time) { if f.items[i].isCollateral { f.items[i].initialFunds = f.items[i].available } - } else if _, ok := f.items[i].snapshot[t.UnixNano()]; ok { + } else if _, ok := f.items[i].snapshot[t.UnixNano()]; !ok { f.items[i].snapshot[t.UnixNano()] = ItemSnapshot{} + // todo investigate this } iss := ItemSnapshot{ diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index 82c55094539..fd08eb71fae 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -108,8 +108,8 @@ type DetailedCandle struct { type linkCurrencyDiff struct { FuturesPair currency.Pair SpotPair currency.Pair - FuturesEvents []statistics.DataAtOffset - SpotEvents []statistics.DataAtOffset + FuturesEvents []*statistics.DataAtOffset //TODO investigate if that did anything + SpotEvents []*statistics.DataAtOffset DiffPercent []decimal.Decimal } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index caf59dfc108..3ecb37fe7e8 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1726,6 +1726,7 @@ {{$pair.Base}} Funds {{$pair.Quote}} Funds Total value in {{$pair.Quote}} + Committed funds in {{$pair.Quote}} {{ end }} @@ -1759,6 +1760,7 @@ {{ $.Prettify.Decimal8 $ev.Holdings.BaseSize}} {{$pair.Base}} {{ $.Prettify.Decimal8 $ev.Holdings.QuoteSize}} {{$pair.Quote}} {{ $.Prettify.Decimal8 $ev.Holdings.TotalValue}} {{$pair.Quote}} + {{ $.Prettify.Decimal8 $ev.Holdings.CommittedFunds}} {{$pair.Quote}} {{end}} {{end}} diff --git a/exchanges/order/futures.go b/exchanges/order/futures.go index 9da635b1862..01ef66120ff 100644 --- a/exchanges/order/futures.go +++ b/exchanges/order/futures.go @@ -344,7 +344,7 @@ func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { if tracker, ok := m.orderPositions[d.ID]; ok { // this has already been associated // update the tracker - return tracker.TrackNewOrder(d) + return tracker.TrackNewOrder(d, false) } if len(m.positions) > 0 { for i := range m.positions { @@ -353,7 +353,7 @@ func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { } } if m.positions[len(m.positions)-1].status == Open { - err := m.positions[len(m.positions)-1].TrackNewOrder(d) + err := m.positions[len(m.positions)-1].TrackNewOrder(d, false) if err != nil && !errors.Is(err, ErrPositionClosed) { return err } @@ -375,7 +375,7 @@ func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error { return err } m.positions = append(m.positions, tracker) - err = tracker.TrackNewOrder(d) + err = tracker.TrackNewOrder(d, true) if err != nil { return err } @@ -518,12 +518,15 @@ func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) { // TrackNewOrder knows how things are going for a given // futures contract -func (p *PositionTracker) TrackNewOrder(d *Detail) error { +func (p *PositionTracker) TrackNewOrder(d *Detail, isInitialOrder bool) error { if p == nil { return fmt.Errorf("position tracker %w", common.ErrNilPointer) } p.m.Lock() defer p.m.Unlock() + if isInitialOrder && len(p.pnlHistory) > 0 { + return fmt.Errorf("%w received isInitialOrder = true with existing position", errCannotTrackInvalidParams) + } if p.status.IsInactive() { return ErrPositionClosed } @@ -539,6 +542,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if p.asset != d.AssetType { return fmt.Errorf("%w asset '%v' received: '%v'", errOrderNotEqualToTracker, d.AssetType, p.asset) } + if d.Side == UnknownSide { return ErrSideIsInvalid } @@ -590,7 +594,8 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount)) } - if p.currentDirection == UnknownSide { + if isInitialOrder { + p.openingDirection = d.Side p.currentDirection = d.Side } @@ -620,8 +625,18 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { if len(p.pnlHistory) != 0 { cal.PreviousPrice = p.pnlHistory[len(p.pnlHistory)-1].Price } - if (cal.OrderDirection.IsShort() && cal.CurrentDirection.IsLong() || cal.OrderDirection.IsLong() && cal.CurrentDirection.IsShort()) && - cal.Exposure.LessThan(amount) { + switch { + case isInitialOrder: + result = &PNLResult{ + IsOrder: true, + Time: cal.Time, + Price: cal.CurrentPrice, + Exposure: cal.Amount, + Fee: cal.Fee, + Direction: cal.OpeningDirection, + UnrealisedPNL: cal.Fee.Neg(), + } + case (cal.OrderDirection.IsShort() && cal.CurrentDirection.IsLong() || cal.OrderDirection.IsLong() && cal.CurrentDirection.IsShort()) && cal.Exposure.LessThan(amount): // latest order swaps directions! // split the order to calculate PNL from each direction first := cal.Exposure @@ -655,7 +670,7 @@ func (p *PositionTracker) TrackNewOrder(d *Detail) error { cal.Time = cal.Time.Add(1) cal.PNLHistory = p.pnlHistory result, err = p.PNLCalculation.CalculatePNL(context.TODO(), cal) - } else { + default: result, err = p.PNLCalculation.CalculatePNL(context.TODO(), cal) } if err != nil { diff --git a/exchanges/order/futures_test.go b/exchanges/order/futures_test.go index 5f0becf9ea5..1704c148e67 100644 --- a/exchanges/order/futures_test.go +++ b/exchanges/order/futures_test.go @@ -89,11 +89,11 @@ func TestTrackNewOrder(t *testing.T) { t.Error(err) } - err = f.TrackNewOrder(nil) + err = f.TrackNewOrder(nil, false) if !errors.Is(err, ErrSubmissionIsNil) { t.Error(err) } - err = f.TrackNewOrder(&Detail{}) + err = f.TrackNewOrder(&Detail{}, false) if !errors.Is(err, errOrderNotEqualToTracker) { t.Error(err) } @@ -105,7 +105,7 @@ func TestTrackNewOrder(t *testing.T) { ID: "1", Price: 1337, } - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, ErrSideIsInvalid) { t.Error(err) } @@ -113,13 +113,13 @@ func TestTrackNewOrder(t *testing.T) { od.Side = Long od.Amount = 1 od.ID = "2" - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, errTimeUnset) { t.Error(err) } f.openingDirection = Long od.Date = time.Now() - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, nil) { t.Error(err) } @@ -140,7 +140,7 @@ func TestTrackNewOrder(t *testing.T) { od.Amount = 0.4 od.Side = Short od.ID = "3" - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, nil) { t.Error(err) } @@ -159,7 +159,7 @@ func TestTrackNewOrder(t *testing.T) { od.Side = Short od.ID = "4" od.Fee = 0.1 - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, nil) { t.Error(err) } @@ -174,7 +174,7 @@ func TestTrackNewOrder(t *testing.T) { od.ID = "5" od.Side = Long od.Amount = 0.2 - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, nil) { t.Error(err) } @@ -185,7 +185,7 @@ func TestTrackNewOrder(t *testing.T) { t.Errorf("expected recognition that its closed, received '%v'", f.status) } - err = f.TrackNewOrder(od) + err = f.TrackNewOrder(od, false) if !errors.Is(err, ErrPositionClosed) { t.Error(err) } @@ -195,6 +195,20 @@ func TestTrackNewOrder(t *testing.T) { if f.status != Closed { t.Errorf("expected recognition that its closed, received '%v'", f.status) } + + err = f.TrackNewOrder(od, true) + if !errors.Is(err, errCannotTrackInvalidParams) { + t.Error(err) + } + + f, err = e.SetupPositionTracker(setup) + if !errors.Is(err, nil) { + t.Error(err) + } + err = f.TrackNewOrder(od, true) + if !errors.Is(err, nil) { + t.Error(err) + } } func TestSetupMultiPositionTracker(t *testing.T) { @@ -1029,7 +1043,7 @@ func TestPositionLiquidate(t *testing.T) { ID: "lol", Price: 1, Amount: 1, - }) + }, false) if !errors.Is(err, nil) { t.Error(err) } diff --git a/exchanges/order/futures_types.go b/exchanges/order/futures_types.go index c7114e0e28b..267ef8dbc83 100644 --- a/exchanges/order/futures_types.go +++ b/exchanges/order/futures_types.go @@ -44,6 +44,7 @@ var ( errNilOrder = errors.New("nil order received") errNoPNLHistory = errors.New("no pnl history") errCannotCalculateUnrealisedPNL = errors.New("cannot calculate unrealised PNL") + errCannotTrackInvalidParams = errors.New("parameters set incorrectly, cannot track") ) // PNLCalculation is an interface to allow multiple From f9bc76db7ec5c6bad8e1ea20d96dd68cbd4168c2 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Wed, 11 May 2022 16:49:24 +1000 Subject: [PATCH 145/171] New base funcs. New order summary --- backtester/common/common_types.go | 4 ++ backtester/eventhandlers/exchange/exchange.go | 46 ++++++++++++------- .../portfolio/holdings/holdings.go | 1 - .../eventhandlers/portfolio/portfolio.go | 12 +---- .../eventhandlers/strategies/base/base.go | 12 +---- .../ftxcashandcarry/ftxcashandcarry.go | 2 +- backtester/eventtypes/event/event.go | 28 +++++++---- backtester/eventtypes/event/event_test.go | 17 ++++++- backtester/eventtypes/event/event_types.go | 1 + backtester/report/tpl.gohtml | 18 ++++++-- 10 files changed, 88 insertions(+), 53 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index a07ef61b4b3..edc1f04e44b 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -5,6 +5,7 @@ import ( "time" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" @@ -35,15 +36,18 @@ var ( // EventHandler interface implements required GetTime() & Pair() return type EventHandler interface { + GetBase() event.Base GetOffset() int64 SetOffset(int64) IsEvent() bool GetTime() time.Time Pair() currency.Pair + GetUnderlyingPair() currency.Pair GetExchange() string GetInterval() kline.Interval GetAssetType() asset.Item GetReason() string + GetReasons() []string GetClosePrice() decimal.Decimal AppendReason(string) AppendReasonf(string, ...interface{}) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 164976e8ff2..22cbea9f514 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -11,7 +11,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange/slippage" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/fill" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/funding" @@ -34,15 +33,7 @@ var ErrDoNothing = errors.New("received Do Nothing direction") // will send an order to the exchange/fake order manager to be stored and raise a fill event func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager *engine.OrderManager, funds funding.IFundReleaser) (fill.Event, error) { f := &fill.Fill{ - Base: event.Base{ - Offset: o.GetOffset(), - Exchange: o.GetExchange(), - Time: o.GetTime(), - CurrencyPair: o.Pair(), - AssetType: o.GetAssetType(), - Interval: o.GetInterval(), - Reason: o.GetReason(), - }, + Base: o.GetBase(), Direction: o.GetDirection(), Amount: o.GetAmount(), ClosePrice: data.Latest().GetClosePrice(), @@ -162,12 +153,6 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if err != nil { return f, err } - if !o.IsLiquidating() { - err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) - if err != nil { - return f, err - } - } ords := orderManager.GetOrdersSnapshot(gctorder.UnknownStatus) for i := range ords { @@ -185,6 +170,12 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * } f.Total = f.PurchasePrice.Mul(f.Amount).Add(f.ExchangeFee) } + if !o.IsLiquidating() { + err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) + if err != nil { + return f, err + } + } if f.Order == nil { return nil, fmt.Errorf("placed order %v not found in order manager", orderID) @@ -220,6 +211,7 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro } return orderError } + switch f.GetDirection() { case gctorder.Buy, gctorder.Bid: err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) @@ -242,6 +234,7 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro default: return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.GetDirection()) } + f.AppendReason(summarisePosition(f.GetDirection(), f.Amount, f.Amount.Mul(f.PurchasePrice), f.ExchangeFee, f.Order.Pair, currency.EMPTYPAIR)) case asset.Futures: cr, err := funds.CollateralReleaser() if err != nil { @@ -262,12 +255,33 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro } return orderError } + f.AppendReason(summarisePosition(f.GetDirection(), f.Amount, f.Amount.Mul(f.PurchasePrice), f.ExchangeFee, f.Order.Pair, f.UnderlyingPair)) default: return fmt.Errorf("%w asset type %v", common.ErrInvalidDataType, f.AssetType) } return nil } +func summarisePosition(direction gctorder.Side, orderAmount, orderTotal, orderFee decimal.Decimal, pair, underlying currency.Pair) string { + baseCurr := pair.Base.String() + quoteCurr := pair.Quote + if !underlying.IsEmpty() { + baseCurr = pair.String() + quoteCurr = underlying.Quote + } + return fmt.Sprintf("Placed %s order of %v %v for %v %v, with %v %v in fees, totalling %v %v", + direction, + orderAmount.Round(8), + baseCurr, + orderTotal.Round(8), + quoteCurr, + orderFee.Round(8), + quoteCurr, + orderTotal.Add(orderFee).Round(8), + quoteCurr, + ) +} + // verifyOrderWithinLimits conforms the amount to fall into the minimum size and maximum size limit after reduced func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, cs *Settings) error { if f == nil { diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index f7299cbbcea..193da3b6ac8 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -9,7 +9,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/log" ) // Create makes a Holding struct to track total values of strategy holdings over the course of a backtesting run diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 91815a00b57..b3e079171c0 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -42,15 +42,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi } o := &order.Order{ - Base: event.Base{ - Offset: ev.GetOffset(), - Exchange: ev.GetExchange(), - Time: ev.GetTime(), - CurrencyPair: ev.Pair(), - AssetType: ev.GetAssetType(), - Interval: ev.GetInterval(), - Reason: ev.GetReason(), - }, + Base: ev.GetBase(), Direction: ev.GetDirection(), FillDependentEvent: ev.GetFillDependentEvent(), Amount: ev.GetAmount(), @@ -659,7 +651,7 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle Time: ev.GetTime(), Interval: ev.GetInterval(), CurrencyPair: pos.Pair, - UnderlyingPair: pos.Pair, + UnderlyingPair: ev.GetUnderlyingPair(), AssetType: pos.Asset, Reason: "LIQUIDATED", }, diff --git a/backtester/eventhandlers/strategies/base/base.go b/backtester/eventhandlers/strategies/base/base.go index efab4f2ed58..f71c19962f7 100644 --- a/backtester/eventhandlers/strategies/base/base.go +++ b/backtester/eventhandlers/strategies/base/base.go @@ -3,7 +3,6 @@ package base import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" - "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" ) @@ -23,16 +22,7 @@ func (s *Strategy) GetBaseData(d data.Handler) (signal.Signal, error) { return signal.Signal{}, common.ErrNilEvent } return signal.Signal{ - Base: event.Base{ - Offset: latest.GetOffset(), - Exchange: latest.GetExchange(), - Time: latest.GetTime(), - CurrencyPair: latest.Pair(), - AssetType: latest.GetAssetType(), - Interval: latest.GetInterval(), - Reason: latest.GetReason(), - UnderlyingPair: latest.GetUnderlyingPair(), - }, + Base: latest.GetBase(), ClosePrice: latest.GetClosePrice(), HighPrice: latest.GetHighPrice(), OpenPrice: latest.GetOpenPrice(), diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index b90fd24bcae..0f9e64fcb3d 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -126,7 +126,7 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS futuresSignal.AppendReason("Shorting to perform cash and carry") futuresSignal.CollateralCurrency = spotSignal.CurrencyPair.Base futuresSignal.MatchesOrderAmount = true - spotSignal.AppendReasonf("Signalling shorting of %v", futuresSignal.Pair()) + spotSignal.AppendReasonf("Signalling shorting of %v after spot order placed", futuresSignal.Pair()) // set the FillDependentEvent to use the futures signal // as the futures signal relies on a completed spot order purchase // to use as collateral diff --git a/backtester/eventtypes/event/event.go b/backtester/eventtypes/event/event.go index 7ebfc5b568b..3e8e7b8ffcf 100644 --- a/backtester/eventtypes/event/event.go +++ b/backtester/eventtypes/event/event.go @@ -35,6 +35,11 @@ func (b *Base) Pair() currency.Pair { return b.CurrencyPair } +// GetUnderlyingPair returns the currency pair +func (b *Base) GetUnderlyingPair() currency.Pair { + return b.UnderlyingPair +} + // GetExchange returns the exchange func (b *Base) GetExchange() string { return strings.ToLower(b.Exchange) @@ -52,25 +57,28 @@ func (b *Base) GetInterval() kline.Interval { // AppendReason adds reasoning for a decision being made func (b *Base) AppendReason(y string) { - if b.Reason == "" { - b.Reason = y - } else { - b.Reason = y + ". " + b.Reason - } + b.Reason += y + ". " + b.Reasons = append(b.Reasons, y) } // AppendReasonf adds reasoning for a decision being made // but with formatting func (b *Base) AppendReasonf(y string, addons ...interface{}) { y = fmt.Sprintf(y, addons...) - if b.Reason == "" { - b.Reason = y - } else { - b.Reason = y + ". " + b.Reason - } + b.Reason += y + ". " + b.Reasons = append(b.Reasons, y) } // GetReason returns the why func (b *Base) GetReason() string { return b.Reason } + +// GetReasons returns each individual reason +func (b *Base) GetReasons() []string { + return b.Reasons +} + +func (b *Base) GetBase() Base { + return *b +} diff --git a/backtester/eventtypes/event/event_test.go b/backtester/eventtypes/event/event_test.go index 362966bdd42..c393eb9a292 100644 --- a/backtester/eventtypes/event/event_test.go +++ b/backtester/eventtypes/event/event_test.go @@ -10,7 +10,7 @@ import ( gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) -func TestEvent_AppendWhy(t *testing.T) { +func TestEvent_GetReason(t *testing.T) { t.Parallel() e := &Base{} e.AppendReason("test") @@ -25,6 +25,21 @@ func TestEvent_AppendWhy(t *testing.T) { } } +func TestEvent_GetReasons(t *testing.T) { + t.Parallel() + e := &Base{} + e.AppendReason("test") + y := e.GetReasons() + if !strings.Contains(y[0], "test") { + t.Error("expected test") + } + e.AppendReason("test2") + y = e.GetReasons() + if y[1] != "test2" { + t.Error("expected 'test2'") + } +} + func TestEvent_GetAssetType(t *testing.T) { t.Parallel() e := &Base{ diff --git a/backtester/eventtypes/event/event_types.go b/backtester/eventtypes/event/event_types.go index db002287843..76c757e1ab4 100644 --- a/backtester/eventtypes/event/event_types.go +++ b/backtester/eventtypes/event/event_types.go @@ -20,4 +20,5 @@ type Base struct { UnderlyingPair currency.Pair `json:"underlying"` AssetType asset.Item `json:"asset"` Reason string `json:"reason"` + Reasons []string `json:"reasons"` } diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 3ecb37fe7e8..67aadd0a1a0 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1716,7 +1716,7 @@ Date Price Action - Why + Event Details {{if $asset.IsFutures}} Holdings Position Direction @@ -1737,12 +1737,24 @@ {{$ev.FillEvent.GetTime}} {{ $.Prettify.Decimal8 $ev.FillEvent.GetClosePrice}} {{if $asset.IsFutures}}{{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}}{{else}}{{$pair.Quote}}{{end}} {{$ev.FillEvent.GetDirection}} - {{$ev.FillEvent.GetReason}} + +
    + {{ range $ev.FillEvent.GetReasons }} +
  • {{.}}
  • + {{end}} +
+ {{ else if ne $ev.SignalEvent nil}} {{$ev.SignalEvent.GetTime}} {{ $.Prettify.Decimal8 $ev.SignalEvent.GetPrice}} {{if $asset.IsFutures}}{{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}}{{else}}{{$pair.Quote}}{{end}} {{$ev.SignalEvent.GetDirection}} - {{$ev.SignalEvent.GetReason}} + +
    + {{ range $ev.SignalEvent.GetReasons }} +
  • {{.}}
  • + {{end}} +
+ {{ end }} {{if $asset.IsFutures}} {{if ne $ev.PNL nil }} From 1df5cc9b3ef838d652bd85eedb6e17dc064b6b0c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 12 May 2022 11:38:36 +1000 Subject: [PATCH 146/171] Fun test updates --- backtester/common/common_types.go | 4 +- backtester/config/config_test.go | 2 +- backtester/data/data_test.go | 54 ++++--- backtester/data/kline/kline.go | 4 +- backtester/data/kline/kline_test.go | 10 +- backtester/engine/backtest_test.go | 25 +-- .../eventhandlers/exchange/exchange_test.go | 12 +- .../portfolio/holdings/holdings_test.go | 24 +-- .../eventhandlers/portfolio/portfolio.go | 8 +- .../eventhandlers/portfolio/portfolio_test.go | 124 ++++++++------- .../eventhandlers/portfolio/risk/risk_test.go | 15 +- .../eventhandlers/portfolio/size/size.go | 11 +- .../eventhandlers/portfolio/size/size_test.go | 46 +++--- .../statistics/currencystatistics_test.go | 4 +- .../eventhandlers/statistics/printresults.go | 8 +- .../eventhandlers/statistics/statistics.go | 2 +- .../statistics/statistics_test.go | 142 ++++++++---------- .../statistics/statistics_types.go | 2 +- .../strategies/base/base_test.go | 2 +- .../dollarcostaverage_test.go | 4 +- .../ftxcashandcarry/ftxcashandcarry_test.go | 12 +- .../eventhandlers/strategies/rsi/rsi_test.go | 4 +- .../top2bottom2/top2bottom2_test.go | 9 +- backtester/eventtypes/event/event.go | 12 +- backtester/eventtypes/event/event_test.go | 48 ++++-- backtester/eventtypes/event/event_types.go | 1 - backtester/eventtypes/fill/fill_types.go | 2 +- backtester/eventtypes/kline/kline_test.go | 2 +- backtester/eventtypes/kline/kline_types.go | 2 +- backtester/eventtypes/order/order_test.go | 4 +- backtester/eventtypes/order/order_types.go | 2 +- backtester/eventtypes/signal/signal_test.go | 16 +- backtester/eventtypes/signal/signal_types.go | 2 +- backtester/funding/funding.go | 10 ++ backtester/funding/funding_test.go | 59 +++++++- backtester/report/report_types.go | 4 +- exchanges/binance/binance_test.go | 2 +- exchanges/ftx/ftx_test.go | 2 +- 38 files changed, 404 insertions(+), 292 deletions(-) diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index edc1f04e44b..0e5409bd1f9 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -36,7 +36,7 @@ var ( // EventHandler interface implements required GetTime() & Pair() return type EventHandler interface { - GetBase() event.Base + GetBase() *event.Base GetOffset() int64 SetOffset(int64) IsEvent() bool @@ -46,7 +46,7 @@ type EventHandler interface { GetExchange() string GetInterval() kline.Interval GetAssetType() asset.Item - GetReason() string + GetConcatReasons() string GetReasons() []string GetClosePrice() decimal.Decimal AppendReason(string) diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 4cee1fe60b2..3d802ed7f75 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -25,7 +25,7 @@ const ( testExchange = "ftx" dca = "dollarcostaverage" // change this if you modify a config and want it to save to the example folder - saveConfig = !false + saveConfig = false ) var ( diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index d342c0ad128..0fc5fe45e61 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -129,7 +129,7 @@ func TestGetDataForCurrency(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d.SetDataForCurrency(testExchange, a, p, nil) d.SetDataForCurrency(testExchange, a, currency.NewPair(currency.BTC, currency.DOGE), nil) - ev := &order.Order{Base: event.Base{ + ev := &order.Order{Base: &event.Base{ Exchange: testExchange, AssetType: a, CurrencyPair: p, @@ -147,7 +147,7 @@ func TestGetDataForCurrency(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, common.ErrNilEvent) } - _, err = d.GetDataForCurrency(&order.Order{Base: event.Base{ + _, err = d.GetDataForCurrency(&order.Order{Base: &event.Base{ Exchange: "lol", AssetType: asset.USDTMarginedFutures, CurrencyPair: currency.NewPair(currency.EMB, currency.DOGE), @@ -156,7 +156,7 @@ func TestGetDataForCurrency(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, ErrHandlerNotFound) } - _, err = d.GetDataForCurrency(&order.Order{Base: event.Base{ + _, err = d.GetDataForCurrency(&order.Order{Base: &event.Base{ Exchange: testExchange, AssetType: asset.USDTMarginedFutures, CurrencyPair: currency.NewPair(currency.EMB, currency.DOGE), @@ -181,62 +181,74 @@ func TestReset(t *testing.T) { } // methods that satisfy the common.DataEventHandler interface -func (t fakeDataHandler) GetOffset() int64 { +func (f fakeDataHandler) GetOffset() int64 { return 4 } -func (t fakeDataHandler) SetOffset(int64) { +func (f fakeDataHandler) SetOffset(int64) { } -func (t fakeDataHandler) IsEvent() bool { +func (f fakeDataHandler) IsEvent() bool { return false } -func (t fakeDataHandler) GetTime() time.Time { - return time.Now().Add(time.Hour * time.Duration(t.time)) +func (f fakeDataHandler) GetTime() time.Time { + return time.Now().Add(time.Hour * time.Duration(f.time)) } -func (t fakeDataHandler) Pair() currency.Pair { +func (f fakeDataHandler) Pair() currency.Pair { return currency.NewPair(currency.BTC, currency.USD) } -func (t fakeDataHandler) GetExchange() string { +func (f fakeDataHandler) GetExchange() string { return "fake" } -func (t fakeDataHandler) GetInterval() kline.Interval { +func (f fakeDataHandler) GetInterval() kline.Interval { return kline.Interval(time.Minute) } -func (t fakeDataHandler) GetAssetType() asset.Item { +func (f fakeDataHandler) GetAssetType() asset.Item { return asset.Spot } -func (t fakeDataHandler) GetReason() string { +func (f fakeDataHandler) GetReason() string { return "fake" } -func (t fakeDataHandler) AppendReason(string) { +func (f fakeDataHandler) AppendReason(string) { } -func (t fakeDataHandler) GetClosePrice() decimal.Decimal { +func (f fakeDataHandler) GetClosePrice() decimal.Decimal { return decimal.Zero } -func (t fakeDataHandler) GetHighPrice() decimal.Decimal { +func (f fakeDataHandler) GetHighPrice() decimal.Decimal { return decimal.Zero } -func (t fakeDataHandler) GetLowPrice() decimal.Decimal { +func (f fakeDataHandler) GetLowPrice() decimal.Decimal { return decimal.Zero } -func (t fakeDataHandler) GetOpenPrice() decimal.Decimal { +func (f fakeDataHandler) GetOpenPrice() decimal.Decimal { return decimal.Zero } -func (t fakeDataHandler) GetUnderlyingPair() currency.Pair { - return t.Pair() +func (f fakeDataHandler) GetUnderlyingPair() currency.Pair { + return f.Pair() } -func (t fakeDataHandler) AppendReasonf(s string, i ...interface{}) {} +func (f fakeDataHandler) AppendReasonf(s string, i ...interface{}) {} + +func (f fakeDataHandler) GetBase() *event.Base { + return &event.Base{} +} + +func (f fakeDataHandler) GetConcatReasons() string { + return "" +} + +func (f fakeDataHandler) GetReasons() []string { + return nil +} diff --git a/backtester/data/kline/kline.go b/backtester/data/kline/kline.go index 0e8fac213de..f4e0b8c59a6 100644 --- a/backtester/data/kline/kline.go +++ b/backtester/data/kline/kline.go @@ -30,7 +30,7 @@ func (d *DataFromKline) Load() error { klineData := make([]common.DataEventHandler, len(d.Item.Candles)) for i := range d.Item.Candles { newKline := &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Offset: int64(i + 1), Exchange: d.Item.Exchange, Time: d.Item.Candles[i].Time.UTC(), @@ -73,7 +73,7 @@ func (d *DataFromKline) AppendResults(ki *gctkline.Item) { candleTimes := make([]time.Time, len(gctCandles)) for i := range gctCandles { klineData[i] = &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Offset: int64(i + 1), Exchange: ki.Exchange, Time: gctCandles[i].Time, diff --git a/backtester/data/kline/kline_test.go b/backtester/data/kline/kline_test.go index 4aae0887988..e6fe0b32213 100644 --- a/backtester/data/kline/kline_test.go +++ b/backtester/data/kline/kline_test.go @@ -140,7 +140,7 @@ func TestStreamOpen(t *testing.T) { } d.SetStream([]common.DataEventHandler{ &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: time.Now(), Interval: gctkline.OneDay, @@ -171,7 +171,7 @@ func TestStreamVolume(t *testing.T) { } d.SetStream([]common.DataEventHandler{ &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: time.Now(), Interval: gctkline.OneDay, @@ -202,7 +202,7 @@ func TestStreamClose(t *testing.T) { } d.SetStream([]common.DataEventHandler{ &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: time.Now(), Interval: gctkline.OneDay, @@ -233,7 +233,7 @@ func TestStreamHigh(t *testing.T) { } d.SetStream([]common.DataEventHandler{ &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: time.Now(), Interval: gctkline.OneDay, @@ -266,7 +266,7 @@ func TestStreamLow(t *testing.T) { } d.SetStream([]common.DataEventHandler{ &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: time.Now(), Interval: gctkline.OneDay, diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 666195169ce..2fcc59c0ee5 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -55,16 +55,7 @@ func (p portfolioOverride) CreateLiquidationOrdersForExchange(ev common.DataEven } return []order.Event{ &order.Order{ - Base: event.Base{ - Offset: ev.GetOffset(), - Exchange: ev.GetExchange(), - Time: ev.GetTime(), - Interval: ev.GetInterval(), - CurrencyPair: ev.Pair(), - UnderlyingPair: ev.GetUnderlyingPair(), - AssetType: ev.GetAssetType(), - Reason: ev.GetReason(), - }, + Base: ev.GetBase(), ID: "1", Direction: gctorder.Short, }, @@ -722,7 +713,7 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { a := asset.Futures expectedError = common.ErrNilArguments ev := &evkline.Kline{ - Base: event.Base{Exchange: testExchange, + Base: &event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } @@ -736,7 +727,7 @@ func TestTriggerLiquidationsForExchange(t *testing.T) { bt.Datas = &data.HandlerPerCurrency{} d := data.Base{} d.SetStream([]common.DataEventHandler{&evkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.OneDay, @@ -803,7 +794,7 @@ func TestUpdateStatsForDataEvent(t *testing.T) { cp := currency.NewPair(currency.BTC, currency.USDT) a := asset.Futures ev := &evkline.Kline{ - Base: event.Base{Exchange: testExchange, + Base: &event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } @@ -890,7 +881,7 @@ func TestProcessSignalEvent(t *testing.T) { cp := currency.NewPair(currency.BTC, currency.USDT) a := asset.Futures de := &evkline.Kline{ - Base: event.Base{Exchange: testExchange, + Base: &event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } @@ -964,7 +955,7 @@ func TestProcessOrderEvent(t *testing.T) { cp := currency.NewPair(currency.BTC, currency.USDT) a := asset.Futures de := &evkline.Kline{ - Base: event.Base{Exchange: testExchange, + Base: &event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } @@ -1084,7 +1075,7 @@ func TestProcessFillEvent(t *testing.T) { cp := currency.NewPair(currency.BTC, currency.USD) a := asset.Futures de := &evkline.Kline{ - Base: event.Base{Exchange: testExchange, + Base: &event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } @@ -1239,7 +1230,7 @@ func TestProcessFuturesFillEvent(t *testing.T) { cp := currency.NewPair(currency.BTC, currency.USD) a := asset.Futures de := &evkline.Kline{ - Base: event.Base{Exchange: testExchange, + Base: &event.Base{Exchange: testExchange, AssetType: a, CurrencyPair: cp}, } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 36f0411059f..fd26592331e 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -171,7 +171,9 @@ func TestPlaceOrder(t *testing.T) { if !errors.Is(err, common.ErrNilEvent) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } - f := &fill.Fill{} + f := &fill.Fill{ + Base: &event.Base{}, + } _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, f, bot.OrderManager) if !errors.Is(err, engine.ErrExchangeNameIsEmpty) { t.Errorf("received: %v, expected: %v", err, engine.ErrExchangeNameIsEmpty) @@ -244,7 +246,7 @@ func TestExecuteOrder(t *testing.T) { MaximumSlippageRate: decimal.NewFromInt(1), } e := Exchange{} - ev := event.Base{ + ev := &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.FifteenMin, @@ -362,7 +364,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { e := Exchange{ CurrencySettings: []Settings{cs}, } - ev := event.Base{ + ev := &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.FifteenMin, @@ -529,6 +531,7 @@ func TestVerifyOrderWithinLimits(t *testing.T) { MaximumSize: decimal.NewFromInt(1), }, } + f.Base = &event.Base{} err = verifyOrderWithinLimits(f, decimal.NewFromFloat(0.5), s) if !errors.Is(err, errExceededPortfolioLimit) { t.Errorf("received %v expected %v", err, errExceededPortfolioLimit) @@ -565,7 +568,7 @@ func TestAllocateFundsPostOrder(t *testing.T) { expectedError = common.ErrNilArguments f := &fill.Fill{ - Base: event.Base{ + Base: &event.Base{ AssetType: asset.Spot, }, Direction: gctorder.Buy, @@ -597,6 +600,7 @@ func TestAllocateFundsPostOrder(t *testing.T) { if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } + f.Order = &gctorder.Detail{} err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index d0f74dfeaa4..f5b85939e6f 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -60,14 +60,14 @@ func TestCreate(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } _, err = Create(&fill.Fill{ - Base: event.Base{AssetType: asset.Spot}, + Base: &event.Base{AssetType: asset.Spot}, }, pair(t)) if err != nil { t.Error(err) } _, err = Create(&fill.Fill{ - Base: event.Base{AssetType: asset.Futures}, + Base: &event.Base{AssetType: asset.Futures}, }, collateral(t)) if err != nil { t.Error(err) @@ -77,14 +77,14 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { t.Parallel() h, err := Create(&fill.Fill{ - Base: event.Base{AssetType: asset.Spot}, + Base: &event.Base{AssetType: asset.Spot}, }, pair(t)) if err != nil { t.Error(err) } t1 := h.Timestamp // nolint:ifshort,nolintlint // false positive and triggers only on Windows err = h.Update(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Time: time.Now(), }, }, pair(t)) @@ -98,14 +98,16 @@ func TestUpdate(t *testing.T) { func TestUpdateValue(t *testing.T) { t.Parallel() + b := &event.Base{AssetType: asset.Spot} h, err := Create(&fill.Fill{ - Base: event.Base{AssetType: asset.Spot}, + Base: b, }, pair(t)) if err != nil { t.Error(err) } h.BaseSize = decimal.NewFromInt(1) h.UpdateValue(&kline.Kline{ + Base: b, Close: decimal.NewFromInt(1337), }) if !h.BaseValue.Equal(decimal.NewFromInt(1337)) { @@ -128,14 +130,14 @@ func TestUpdateBuyStats(t *testing.T) { t.Fatal(err) } h, err := Create(&fill.Fill{ - Base: event.Base{AssetType: asset.Spot}, + Base: &event.Base{AssetType: asset.Spot}, }, pair(t)) if err != nil { t.Error(err) } err = h.update(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.OneHour, @@ -193,7 +195,7 @@ func TestUpdateBuyStats(t *testing.T) { } err = h.update(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.OneHour, @@ -256,13 +258,13 @@ func TestUpdateSellStats(t *testing.T) { } h, err := Create(&fill.Fill{ - Base: event.Base{AssetType: asset.Spot}, + Base: &event.Base{AssetType: asset.Spot}, }, p) if err != nil { t.Error(err) } err = h.update(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.OneHour, @@ -322,7 +324,7 @@ func TestUpdateSellStats(t *testing.T) { } err = h.update(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, Time: time.Now(), Interval: gctkline.OneHour, diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index b3e079171c0..2538e80cbf0 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -645,7 +645,7 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle direction = gctorder.Long } closingOrders = append(closingOrders, &order.Order{ - Base: event.Base{ + Base: &event.Base{ Offset: ev.GetOffset(), Exchange: pos.Exchange, Time: ev.GetTime(), @@ -653,7 +653,7 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle CurrencyPair: pos.Pair, UnderlyingPair: ev.GetUnderlyingPair(), AssetType: pos.Asset, - Reason: "LIQUIDATED", + Reasons: []string{"LIQUIDATED"}, }, Direction: direction, Status: gctorder.Liquidated, @@ -675,14 +675,14 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle continue } closingOrders = append(closingOrders, &order.Order{ - Base: event.Base{ + Base: &event.Base{ Offset: ev.GetOffset(), Exchange: ev.GetExchange(), Time: ev.GetTime(), Interval: ev.GetInterval(), CurrencyPair: pair, AssetType: item, - Reason: "LIQUIDATED", + Reasons: []string{"LIQUIDATED"}, }, Direction: gctorder.Sell, Status: gctorder.Liquidated, diff --git a/backtester/eventhandlers/portfolio/portfolio_test.go b/backtester/eventhandlers/portfolio/portfolio_test.go index 37e2b4a183c..0b6625f20b3 100644 --- a/backtester/eventhandlers/portfolio/portfolio_test.go +++ b/backtester/eventhandlers/portfolio/portfolio_test.go @@ -206,7 +206,7 @@ func TestViewHoldingAtTimePeriod(t *testing.T) { p := Portfolio{} tt := time.Now() s := &signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Time: tt, Exchange: testExchange, AssetType: asset.Spot, @@ -265,20 +265,23 @@ func TestUpdate(t *testing.T) { if !errors.Is(err, funding.ErrFundsNotFound) { t.Errorf("received '%v' expected '%v'", err, funding.ErrFundsNotFound) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) + bc, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) + qc, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(100), decimal.Zero) if err != nil { t.Fatal(err) } - pair, err := funding.CreatePair(b, q) + pair, err := funding.CreatePair(bc, qc) if !errors.Is(err, nil) { t.Errorf("received '%v' expected '%v'", err, nil) } - err = p.UpdateHoldings(&kline.Kline{}, pair) + b := &event.Base{} + err = p.UpdateHoldings(&kline.Kline{ + Base: b, + }, pair) if !errors.Is(err, errExchangeUnset) { t.Errorf("received '%v' expected '%v'", err, errExchangeUnset) } @@ -289,7 +292,8 @@ func TestUpdate(t *testing.T) { Exchange: testExchange, Asset: asset.Spot, Pair: currency.NewPair(currency.BTC, currency.USD), - Timestamp: tt}, false) + Timestamp: tt, + }, false) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } @@ -300,14 +304,12 @@ func TestUpdate(t *testing.T) { if err != nil { t.Error(err) } - + b.Time = tt + b.Exchange = testExchange + b.CurrencyPair = currency.NewPair(currency.BTC, currency.USD) + b.AssetType = asset.Spot err = p.UpdateHoldings(&kline.Kline{ - Base: event.Base{ - Time: tt, - Exchange: testExchange, - CurrencyPair: currency.NewPair(currency.BTC, currency.USD), - AssetType: asset.Spot, - }, + Base: b, }, pair) if err != nil { t.Error(err) @@ -368,7 +370,9 @@ func TestAddComplianceSnapshot(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } - err = p.addComplianceSnapshot(&fill.Fill{}) + err = p.addComplianceSnapshot(&fill.Fill{ + Base: &event.Base{}, + }) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } @@ -381,7 +385,7 @@ func TestAddComplianceSnapshot(t *testing.T) { } err = p.addComplianceSnapshot(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, @@ -406,7 +410,7 @@ func TestOnFill(t *testing.T) { } f := &fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), AssetType: asset.Spot, @@ -459,8 +463,10 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, common.ErrNilArguments) { t.Error(err) } - - s := &signal.Signal{} + b := &event.Base{} + s := &signal.Signal{ + Base: b, + } _, err = p.OnSignal(s, &exchange.Settings{}, nil) if !errors.Is(err, errSizeManagerUnset) { t.Errorf("received: %v, expected: %v", err, errSizeManagerUnset) @@ -478,15 +484,15 @@ func TestOnSignal(t *testing.T) { if !errors.Is(err, funding.ErrFundsNotFound) { t.Errorf("received: %v, expected: %v", err, funding.ErrFundsNotFound) } - b, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) + bc, err := funding.CreateItem(testExchange, asset.Spot, currency.BTC, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Fatal(err) } - q, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) + qc, err := funding.CreateItem(testExchange, asset.Spot, currency.USDT, decimal.NewFromInt(1337), decimal.Zero) if err != nil { t.Fatal(err) } - pair, err := funding.CreatePair(b, q) + pair, err := funding.CreatePair(bc, qc) if err != nil { t.Fatal(err) } @@ -506,12 +512,11 @@ func TestOnSignal(t *testing.T) { if err != nil { t.Error(err) } + b.Exchange = testExchange + b.CurrencyPair = currency.NewPair(currency.BTC, currency.USD) + b.AssetType = asset.Spot s = &signal.Signal{ - Base: event.Base{ - Exchange: testExchange, - CurrencyPair: currency.NewPair(currency.BTC, currency.USD), - AssetType: asset.Spot, - }, + Base: b, Direction: gctorder.Buy, } var resp *order.Order @@ -519,7 +524,7 @@ func TestOnSignal(t *testing.T) { if err != nil { t.Fatal(err) } - if resp.Reason == "" { + if len(resp.Reasons) != 2 { t.Error("expected issue") } @@ -528,7 +533,7 @@ func TestOnSignal(t *testing.T) { if err != nil { t.Error(err) } - if resp.Reason == "" { + if len(resp.Reasons) != 4 { t.Error("expected issue") } @@ -592,7 +597,10 @@ func TestGetLatestHoldings(t *testing.T) { func TestGetSnapshotAtTime(t *testing.T) { t.Parallel() p := Portfolio{} - _, err := p.GetLatestOrderSnapshotForEvent(&kline.Kline{}) + b := &event.Base{} + _, err := p.GetLatestOrderSnapshotForEvent(&kline.Kline{ + Base: b, + }) if !errors.Is(err, errNoPortfolioSettings) { t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings) } @@ -623,14 +631,13 @@ func TestGetSnapshotAtTime(t *testing.T) { if !errors.Is(err, nil) { t.Errorf("received: %v, expected: %v", err, nil) } + b.Exchange = testExchange + b.Time = tt + b.Interval = gctkline.OneDay + b.CurrencyPair = cp + b.AssetType = asset.Spot e := &kline.Kline{ - Base: event.Base{ - Exchange: testExchange, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: cp, - AssetType: asset.Spot, - }, + Base: b, } ss, err := p.GetLatestOrderSnapshotForEvent(e) @@ -713,7 +720,9 @@ func TestGetLatestSnapshot(t *testing.T) { func TestCalculatePNL(t *testing.T) { p := &Portfolio{} - ev := &kline.Kline{} + ev := &kline.Kline{ + Base: &event.Base{}, + } err := p.UpdatePNL(ev, decimal.Zero) if !errors.Is(err, gctorder.ErrNotFuturesAsset) { t.Errorf("received: %v, expected: %v", err, gctorder.ErrNotFuturesAsset) @@ -894,7 +903,7 @@ func TestTrackFuturesOrder(t *testing.T) { _, err = p.TrackFuturesOrder(&fill.Fill{ Order: od, - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, AssetType: asset.Futures, CurrencyPair: cp, @@ -937,7 +946,7 @@ func TestGetPositions(t *testing.T) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } ev := &fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), AssetType: asset.Futures, @@ -965,7 +974,7 @@ func TestGetLatestPNLForEvent(t *testing.T) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } ev := &fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), AssetType: asset.Futures, @@ -1019,16 +1028,19 @@ func TestGetFuturesSettingsFromEvent(t *testing.T) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } expectedError = gctorder.ErrNotFuturesAsset - _, err = p.getFuturesSettingsFromEvent(&fill.Fill{}) + b := &event.Base{} + + _, err = p.getFuturesSettingsFromEvent(&fill.Fill{ + Base: b, + }) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } + b.Exchange = testExchange + b.CurrencyPair = currency.NewPair(currency.BTC, currency.USDT) + b.AssetType = asset.Futures ev := &fill.Fill{ - Base: event.Base{ - Exchange: testExchange, - CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), - AssetType: asset.Futures, - }, + Base: b, } expectedError = errExchangeUnset _, err = p.getFuturesSettingsFromEvent(ev) @@ -1064,7 +1076,7 @@ func TestGetLatestPNLs(t *testing.T) { t.Error("expected empty") } ev := &fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: testExchange, CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), AssetType: asset.Futures, @@ -1247,14 +1259,18 @@ func TestCannotPurchase(t *testing.T) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - s := &signal.Signal{} + s := &signal.Signal{ + Base: &event.Base{}, + } expectedError = common.ErrNilArguments _, err = cannotPurchase(s, nil) if !errors.Is(err, expectedError) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - o := &order.Order{} + o := &order.Order{ + Base: &event.Base{}, + } s.Direction = gctorder.Buy expectedError = nil result, err := cannotPurchase(s, o) @@ -1316,7 +1332,11 @@ func TestCreateLiquidationOrdersForExchange(t *testing.T) { t.Fatalf("received '%v' expected '%v'", err, expectedError) } - ev := &kline.Kline{} + b := &event.Base{} + + ev := &kline.Kline{ + Base: b, + } expectedError = common.ErrNilArguments _, err = p.CreateLiquidationOrdersForExchange(ev, nil) if !errors.Is(err, expectedError) { @@ -1418,7 +1438,9 @@ func TestCheckLiquidationStatus(t *testing.T) { t.Errorf("received '%v', expected '%v'", err, expectedError) } - ev := &kline.Kline{} + ev := &kline.Kline{ + Base: &event.Base{}, + } expectedError = common.ErrNilArguments err = p.CheckLiquidationStatus(ev, nil, nil) if !errors.Is(err, expectedError) { diff --git a/backtester/eventhandlers/portfolio/risk/risk_test.go b/backtester/eventhandlers/portfolio/risk/risk_test.go index a345bdfa986..3136fb4b2d5 100644 --- a/backtester/eventhandlers/portfolio/risk/risk_test.go +++ b/backtester/eventhandlers/portfolio/risk/risk_test.go @@ -8,6 +8,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/compliance" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" + "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/event" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -56,15 +57,17 @@ func TestEvaluateOrder(t *testing.T) { if !errors.Is(err, common.ErrNilArguments) { t.Error(err) } - - o := &order.Order{} - h := []holdings.Holding{} p := currency.NewPair(currency.BTC, currency.USDT) e := "binance" a := asset.Spot - o.Exchange = e - o.AssetType = a - o.CurrencyPair = p + o := &order.Order{ + Base: &event.Base{ + Exchange: e, + AssetType: a, + CurrencyPair: p, + }, + } + h := []holdings.Holding{} r.CurrencySettings = make(map[string]map[asset.Item]map[currency.Pair]*CurrencySettings) r.CurrencySettings[e] = make(map[asset.Item]map[currency.Pair]*CurrencySettings) r.CurrencySettings[e][a] = make(map[currency.Pair]*CurrencySettings) diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 5e88218108c..c55b7a51348 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -62,9 +62,8 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc return retOrder, estFee, nil } -func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (decimal.Decimal, decimal.Decimal, error) { - var amount, fee, portfolioAmount, portfolioFee decimal.Decimal - var err error +func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (amount, fee decimal.Decimal, err error) { + var portfolioAmount, portfolioFee decimal.Decimal switch direction { case gctorder.ClosePosition: amount = amountAvailable @@ -122,14 +121,13 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d // that is allowed to be spent/sold for an event. // As fee calculation occurs during the actual ordering process // this can only attempt to factor the potential fee to remain under the max rules -func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal.Decimal, minMaxSettings exchange.MinMax) (decimal.Decimal, decimal.Decimal, error) { +func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal.Decimal, minMaxSettings exchange.MinMax) (amount, fee decimal.Decimal, err error) { if availableFunds.LessThanOrEqual(decimal.Zero) { return decimal.Decimal{}, decimal.Decimal{}, errNoFunds } if price.IsZero() { return decimal.Decimal{}, decimal.Decimal{}, nil } - var amount, fee decimal.Decimal amount = availableFunds.Mul(decimal.NewFromInt(1).Sub(feeRate)).Div(price) if !buyLimit.IsZero() && buyLimit.GreaterThanOrEqual(minMaxSettings.MinimumSize) && @@ -156,7 +154,7 @@ func (s *Size) calculateBuySize(price, availableFunds, feeRate, buyLimit decimal // eg BTC-USD baseAmount will be BTC to be sold // As fee calculation occurs during the actual ordering process // this can only attempt to factor the potential fee to remain under the max rules -func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.Decimal, minMaxSettings exchange.MinMax) (decimal.Decimal, decimal.Decimal, error) { +func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.Decimal, minMaxSettings exchange.MinMax) (amount, fee decimal.Decimal, err error) { if baseAmount.LessThanOrEqual(decimal.Zero) { return decimal.Decimal{}, decimal.Decimal{}, errNoFunds } @@ -164,7 +162,6 @@ func (s *Size) calculateSellSize(price, baseAmount, feeRate, sellLimit decimal.D return decimal.Decimal{}, decimal.Decimal{}, nil } oneMFeeRate := decimal.NewFromInt(1).Sub(feeRate) - var amount, fee decimal.Decimal amount = baseAmount.Mul(oneMFeeRate) if !sellLimit.IsZero() && sellLimit.GreaterThanOrEqual(minMaxSettings.MinimumSize) && diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index 854c85de413..aac35d91a0c 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -32,7 +32,7 @@ func TestSizingAccuracy(t *testing.T) { availableFunds := decimal.NewFromInt(11) feeRate := decimal.NewFromFloat(0.02) buyLimit := decimal.NewFromInt(1) - amountWithoutFee, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) + amountWithoutFee, _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) if err != nil { t.Error(err) } @@ -56,7 +56,7 @@ func TestSizingOverMaxSize(t *testing.T) { availableFunds := decimal.NewFromInt(1338) feeRate := decimal.NewFromFloat(0.02) buyLimit := decimal.NewFromInt(1) - amount, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) + amount, _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) if err != nil { t.Error(err) } @@ -80,7 +80,7 @@ func TestSizingUnderMinSize(t *testing.T) { availableFunds := decimal.NewFromInt(1338) feeRate := decimal.NewFromFloat(0.02) buyLimit := decimal.NewFromInt(1) - _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) + _, _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) if !errors.Is(err, errLessThanMinimum) { t.Errorf("received: %v, expected: %v", err, errLessThanMinimum) } @@ -100,7 +100,7 @@ func TestMaximumBuySizeEqualZero(t *testing.T) { availableFunds := decimal.NewFromInt(13380) feeRate := decimal.NewFromFloat(0.02) buyLimit := decimal.NewFromInt(1) - amount, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) + amount, _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) if amount != buyLimit || err != nil { t.Errorf("expected: %v, received %v, err: %+v", buyLimit, amount, err) } @@ -119,7 +119,7 @@ func TestMaximumSellSizeEqualZero(t *testing.T) { availableFunds := decimal.NewFromInt(13380) feeRate := decimal.NewFromFloat(0.02) sellLimit := decimal.NewFromInt(1) - amount, err := sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) + amount, _, err := sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) if amount != sellLimit || err != nil { t.Errorf("expected: %v, received %v, err: %+v", sellLimit, amount, err) } @@ -140,7 +140,7 @@ func TestSizingErrors(t *testing.T) { availableFunds := decimal.Zero feeRate := decimal.NewFromFloat(0.02) buyLimit := decimal.NewFromInt(1) - _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) + _, _, err := sizer.calculateBuySize(price, availableFunds, feeRate, buyLimit, globalMinMax) if !errors.Is(err, errNoFunds) { t.Errorf("received: %v, expected: %v", err, errNoFunds) } @@ -161,32 +161,38 @@ func TestCalculateSellSize(t *testing.T) { availableFunds := decimal.Zero feeRate := decimal.NewFromFloat(0.02) sellLimit := decimal.NewFromInt(1) - _, err := sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) + _, _, err := sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) if !errors.Is(err, errNoFunds) { t.Errorf("received: %v, expected: %v", err, errNoFunds) } availableFunds = decimal.NewFromInt(1337) - _, err = sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) + _, _, err = sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) if !errors.Is(err, errLessThanMinimum) { t.Errorf("received: %v, expected: %v", err, errLessThanMinimum) } price = decimal.NewFromInt(12) availableFunds = decimal.NewFromInt(1339) - _, err = sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) + amount, fee, err := sizer.calculateSellSize(price, availableFunds, feeRate, sellLimit, globalMinMax) if err != nil { t.Error(err) } + if !amount.Equal(sellLimit) { + t.Errorf("received '%v' expected '%v'", amount, sellLimit) + } + if !amount.Mul(price).Mul(feeRate).Equal(fee) { + t.Errorf("received '%v' expected '%v'", amount.Mul(price).Mul(feeRate), fee) + } } func TestSizeOrder(t *testing.T) { t.Parallel() s := Size{} - _, err := s.SizeOrder(nil, decimal.Zero, nil) + _, _, err := s.SizeOrder(nil, decimal.Zero, nil) if !errors.Is(err, common.ErrNilArguments) { t.Error(err) } o := &order.Order{ - Base: event.Base{ + Base: &event.Base{ Offset: 1, Exchange: "ftx", Time: time.Now(), @@ -196,17 +202,17 @@ func TestSizeOrder(t *testing.T) { }, } cs := &exchange.Settings{} - _, err = s.SizeOrder(o, decimal.Zero, cs) + _, _, err = s.SizeOrder(o, decimal.Zero, cs) if !errors.Is(err, errNoFunds) { t.Errorf("received: %v, expected: %v", err, errNoFunds) } - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) } o.Direction = gctorder.Buy - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) } @@ -214,27 +220,27 @@ func TestSizeOrder(t *testing.T) { o.ClosePrice = decimal.NewFromInt(1) s.BuySide.MaximumSize = decimal.NewFromInt(1) s.BuySide.MinimumSize = decimal.NewFromInt(1) - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { t.Error(err) } o.Amount = decimal.NewFromInt(1) o.Direction = gctorder.Sell - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { t.Error(err) } s.SellSide.MaximumSize = decimal.NewFromInt(1) s.SellSide.MinimumSize = decimal.NewFromInt(1) - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { t.Error(err) } o.Direction = gctorder.ClosePosition - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { t.Error(err) } @@ -251,13 +257,13 @@ func TestSizeOrder(t *testing.T) { t.Error(err) } cs.Exchange = &exch - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if err != nil { t.Error(err) } o.ClosePrice = decimal.NewFromInt(1000000000) - _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) + _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) } diff --git a/backtester/eventhandlers/statistics/currencystatistics_test.go b/backtester/eventhandlers/statistics/currencystatistics_test.go index 0ee0af42a01..9970dea5be8 100644 --- a/backtester/eventhandlers/statistics/currencystatistics_test.go +++ b/backtester/eventhandlers/statistics/currencystatistics_test.go @@ -28,7 +28,7 @@ func TestCalculateResults(t *testing.T) { tt2 := time.Now().Add(gctkline.OneDay.Duration()) exch := testExchange p := currency.NewPair(currency.BTC, currency.USDT) - even := event.Base{ + even := &event.Base{ Exchange: exch, Time: tt1, Interval: gctkline.OneDay, @@ -163,7 +163,7 @@ func TestPrintResults(t *testing.T) { exch := testExchange a := asset.Spot p := currency.NewPair(currency.BTC, currency.USDT) - even := event.Base{ + even := &event.Base{ Exchange: exch, Time: tt1, Interval: gctkline.OneDay, diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index 08144279abf..fb48116ed4a 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -94,7 +94,7 @@ func (s *Statistic) PrintAllEventsChronologically() { fSIL(currencyStatistic.Events[i].FillEvent.Pair().String(), limit14), currencyStatistic.Events[i].FillEvent.GetClosePrice().Round(8), currencyStatistic.Events[i].FillEvent.GetDirection()) - msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) + msg = addReason(currencyStatistic.Events[i].FillEvent.GetConcatReasons(), msg) msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) } else { @@ -114,7 +114,7 @@ func (s *Statistic) PrintAllEventsChronologically() { currencyStatistic.Events[i].FillEvent.GetAmount().Round(8), currencyStatistic.Events[i].FillEvent.GetExchangeFee(), currencyStatistic.Events[i].FillEvent.GetTotal().Round(8)) - msg = addReason(currencyStatistic.Events[i].FillEvent.GetReason(), msg) + msg = addReason(currencyStatistic.Events[i].FillEvent.GetConcatReasons(), msg) msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].FillEvent.GetTime(), msg) } @@ -125,7 +125,7 @@ func (s *Statistic) PrintAllEventsChronologically() { fSIL(a.String(), limit10), fSIL(currencyStatistic.Events[i].SignalEvent.Pair().String(), limit14), currencyStatistic.Events[i].SignalEvent.GetClosePrice().Round(8)) - msg = addReason(currencyStatistic.Events[i].SignalEvent.GetReason(), msg) + msg = addReason(currencyStatistic.Events[i].SignalEvent.GetConcatReasons(), msg) msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].SignalEvent.GetTime(), msg) case currencyStatistic.Events[i].DataEvent != nil: @@ -135,7 +135,7 @@ func (s *Statistic) PrintAllEventsChronologically() { fSIL(a.String(), limit10), fSIL(currencyStatistic.Events[i].DataEvent.Pair().String(), limit14), currencyStatistic.Events[i].DataEvent.GetClosePrice().Round(8)) - msg = addReason(currencyStatistic.Events[i].DataEvent.GetReason(), msg) + msg = addReason(currencyStatistic.Events[i].DataEvent.GetConcatReasons(), msg) msg += common.ColourDefault results = addEventOutputToTime(results, currencyStatistic.Events[i].DataEvent.GetTime(), msg) default: diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index d2d7c1137e6..23bc6dcd150 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -46,7 +46,7 @@ func (s *Statistic) SetupEventForTime(ev common.DataEventHandler) error { } } lookup.Events = append(lookup.Events, - &DataAtOffset{ + DataAtOffset{ DataEvent: ev, Offset: ev.GetOffset(), Time: ev.GetTime(), diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 737c41aadb2..b5639710103 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -55,7 +55,7 @@ func TestAddDataEventForTime(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -96,19 +96,20 @@ func TestAddSignalEventForTime(t *testing.T) { } s.setupMap(exch, a) s.ExchangeAssetPairStatistics = make(map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic) - err = s.SetEventForOffset(&signal.Signal{}) + b := &event.Base{} + err = s.SetEventForOffset(&signal.Signal{ + Base: b, + }) if !errors.Is(err, errCurrencyStatisticsUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyStatisticsUnset) } - + b.Exchange = exch + b.Time = tt + b.Interval = gctkline.OneDay + b.CurrencyPair = p + b.AssetType = a err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, Open: eleet, Close: eleet, Low: eleet, @@ -119,13 +120,7 @@ func TestAddSignalEventForTime(t *testing.T) { t.Error(err) } err = s.SetEventForOffset(&signal.Signal{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, ClosePrice: eleet, Direction: gctorder.Buy, }) @@ -151,19 +146,20 @@ func TestAddExchangeEventForTime(t *testing.T) { } s.setupMap(exch, a) s.ExchangeAssetPairStatistics = make(map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic) - err = s.SetEventForOffset(&order.Order{}) + b := &event.Base{} + err = s.SetEventForOffset(&order.Order{ + Base: b, + }) if !errors.Is(err, errCurrencyStatisticsUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyStatisticsUnset) } - + b.Exchange = exch + b.Time = tt + b.Interval = gctkline.OneDay + b.CurrencyPair = p + b.AssetType = a err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, Open: eleet, Close: eleet, Low: eleet, @@ -174,13 +170,7 @@ func TestAddExchangeEventForTime(t *testing.T) { t.Error(err) } err = s.SetEventForOffset(&order.Order{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, ID: "elite", Direction: gctorder.Buy, Status: gctorder.New, @@ -211,19 +201,22 @@ func TestAddFillEventForTime(t *testing.T) { } s.setupMap(exch, a) s.ExchangeAssetPairStatistics = make(map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic) - err = s.SetEventForOffset(&fill.Fill{}) + b := &event.Base{} + err = s.SetEventForOffset(&fill.Fill{ + Base: b, + }) if !errors.Is(err, errCurrencyStatisticsUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyStatisticsUnset) } + b.Exchange = exch + b.Time = tt + b.Interval = gctkline.OneDay + b.CurrencyPair = p + b.AssetType = a + err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, Open: eleet, Close: eleet, Low: eleet, @@ -234,13 +227,7 @@ func TestAddFillEventForTime(t *testing.T) { t.Error(err) } err = s.SetEventForOffset(&fill.Fill{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, Direction: gctorder.Buy, Amount: eleet, ClosePrice: eleet, @@ -272,7 +259,7 @@ func TestAddHoldingsForTime(t *testing.T) { } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -335,19 +322,18 @@ func TestAddComplianceSnapshotForTime(t *testing.T) { } s.setupMap(exch, a) s.ExchangeAssetPairStatistics = make(map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic) - err = s.AddComplianceSnapshotForTime(compliance.Snapshot{}, &fill.Fill{}) + b := &event.Base{} + err = s.AddComplianceSnapshotForTime(compliance.Snapshot{}, &fill.Fill{Base: b}) if !errors.Is(err, errCurrencyStatisticsUnset) { t.Errorf("received: %v, expected: %v", err, errCurrencyStatisticsUnset) } - + b.Exchange = exch + b.Time = tt + b.Interval = gctkline.OneDay + b.CurrencyPair = p + b.AssetType = a err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, Open: eleet, Close: eleet, Low: eleet, @@ -360,13 +346,7 @@ func TestAddComplianceSnapshotForTime(t *testing.T) { err = s.AddComplianceSnapshotForTime(compliance.Snapshot{ Timestamp: tt, }, &fill.Fill{ - Base: event.Base{ - Exchange: exch, - Time: tt, - Interval: gctkline.OneDay, - CurrencyPair: p, - AssetType: a, - }, + Base: b, }) if err != nil { t.Error(err) @@ -517,7 +497,7 @@ func TestPrintAllEventsChronologically(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -535,7 +515,7 @@ func TestPrintAllEventsChronologically(t *testing.T) { } err = s.SetEventForOffset(&fill.Fill{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -555,7 +535,7 @@ func TestPrintAllEventsChronologically(t *testing.T) { } err = s.SetEventForOffset(&signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -591,7 +571,7 @@ func TestCalculateTheResults(t *testing.T) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -609,7 +589,7 @@ func TestCalculateTheResults(t *testing.T) { t.Error(err) } err = s.SetEventForOffset(&signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -628,7 +608,7 @@ func TestCalculateTheResults(t *testing.T) { t.Error(err) } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -647,7 +627,7 @@ func TestCalculateTheResults(t *testing.T) { } err = s.SetEventForOffset(&signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, @@ -667,7 +647,7 @@ func TestCalculateTheResults(t *testing.T) { } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt2, Interval: gctkline.OneDay, @@ -685,7 +665,7 @@ func TestCalculateTheResults(t *testing.T) { t.Error(err) } err = s.SetEventForOffset(&signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt2, Interval: gctkline.OneDay, @@ -705,7 +685,7 @@ func TestCalculateTheResults(t *testing.T) { } err = s.SetupEventForTime(&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt2, Interval: gctkline.OneDay, @@ -723,7 +703,7 @@ func TestCalculateTheResults(t *testing.T) { t.Error(err) } err = s.SetEventForOffset(&signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt2, Interval: gctkline.OneDay, @@ -821,7 +801,7 @@ func TestCalculateBiggestEventDrawdown(t *testing.T) { var events []common.DataEventHandler for i := int64(0); i < 100; i++ { tt1 = tt1.Add(gctkline.OneDay.Duration()) - even := event.Base{ + even := &event.Base{ Exchange: exch, Time: tt1, Interval: gctkline.OneDay, @@ -847,7 +827,7 @@ func TestCalculateBiggestEventDrawdown(t *testing.T) { } tt1 = tt1.Add(gctkline.OneDay.Duration()) - even := event.Base{ + even := &event.Base{ Exchange: exch, Time: tt1, Interval: gctkline.OneDay, @@ -862,7 +842,7 @@ func TestCalculateBiggestEventDrawdown(t *testing.T) { }) tt1 = tt1.Add(gctkline.OneDay.Duration()) - even = event.Base{ + even = &event.Base{ Exchange: exch, Time: tt1, Interval: gctkline.OneDay, @@ -877,7 +857,7 @@ func TestCalculateBiggestEventDrawdown(t *testing.T) { }) tt1 = tt1.Add(gctkline.OneDay.Duration()) - even = event.Base{ + even = &event.Base{ Exchange: exch, Time: tt1, Interval: gctkline.OneDay, @@ -907,7 +887,7 @@ func TestCalculateBiggestEventDrawdown(t *testing.T) { // bogus scenario bogusEvent := []common.DataEventHandler{ &kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, CurrencyPair: p, AssetType: a, diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index f91d22343e3..0181c6d3326 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -170,7 +170,7 @@ type CurrencyPairStatistic struct { TotalValueLostToSlippage decimal.Decimal TotalValueLost decimal.Decimal - Events []*DataAtOffset `json:"-"` + Events []DataAtOffset `json:"-"` MaxDrawdown Swing `json:"max-drawdown,omitempty"` HighestCommittedFunds ValueAtTime `json:"highest-committed-funds"` diff --git a/backtester/eventhandlers/strategies/base/base_test.go b/backtester/eventhandlers/strategies/base/base_test.go index e82af1296b2..0dd9ca7ae05 100644 --- a/backtester/eventhandlers/strategies/base/base_test.go +++ b/backtester/eventhandlers/strategies/base/base_test.go @@ -34,7 +34,7 @@ func TestGetBase(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&kline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: tt, Interval: gctkline.OneDay, diff --git a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go index 22c4ee281ba..2a9d675e72e 100644 --- a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go +++ b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go @@ -56,7 +56,7 @@ func TestOnSignal(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: dInsert, Interval: gctkline.OneDay, @@ -134,7 +134,7 @@ func TestOnSignals(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Offset: 1, Exchange: exch, Time: dInsert, diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index d5f9f7fe90d..7b5a5722e9d 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -114,7 +114,7 @@ func TestSortSignals(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: dInsert, Interval: gctkline.OneDay, @@ -140,7 +140,7 @@ func TestSortSignals(t *testing.T) { d2 := data.Base{} d2.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: dInsert, Interval: gctkline.OneDay, @@ -176,7 +176,7 @@ func TestCreateSignals(t *testing.T) { } spotSignal := &signal.Signal{ - Base: event.Base{AssetType: asset.Spot}, + Base: &event.Base{AssetType: asset.Spot}, } _, err = s.createSignals(nil, spotSignal, nil, decimal.Zero, false) if !errors.Is(err, expectedError) { @@ -186,7 +186,7 @@ func TestCreateSignals(t *testing.T) { // case len(pos) == 0: expectedError = nil futuresSignal := &signal.Signal{ - Base: event.Base{AssetType: asset.Futures}, + Base: &event.Base{AssetType: asset.Futures}, } resp, err := s.createSignals(nil, spotSignal, futuresSignal, decimal.Zero, false) if !errors.Is(err, expectedError) { @@ -334,7 +334,7 @@ func TestOnSimultaneousSignals(t *testing.T) { } tt := time.Now() d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exchangeName, Time: tt, Interval: gctkline.OneDay, @@ -376,7 +376,7 @@ func TestOnSimultaneousSignals(t *testing.T) { }, } d2.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exchangeName, Time: tt, Interval: gctkline.OneDay, diff --git a/backtester/eventhandlers/strategies/rsi/rsi_test.go b/backtester/eventhandlers/strategies/rsi/rsi_test.go index df7b44a20c8..7f3ee802ec1 100644 --- a/backtester/eventhandlers/strategies/rsi/rsi_test.go +++ b/backtester/eventhandlers/strategies/rsi/rsi_test.go @@ -97,7 +97,7 @@ func TestOnSignal(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Offset: 3, Exchange: exch, Time: dInsert, @@ -179,7 +179,7 @@ func TestOnSignals(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: dInsert, Interval: gctkline.OneDay, diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go index 15702f5930a..989642d1c19 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go @@ -111,7 +111,7 @@ func TestOnSignals(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USDT) d := data.Base{} d.SetStream([]common.DataEventHandler{&eventkline.Kline{ - Base: event.Base{ + Base: &event.Base{ Exchange: exch, Time: dInsert, Interval: gctkline.OneDay, @@ -166,10 +166,11 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { if err != nil { t.Error(err) } - + b := &event.Base{} fundEvents := []mfiFundEvent{ { event: &signal.Signal{ + Base: b, ClosePrice: decimal.NewFromInt(99), Direction: order.DoNothing, }, @@ -177,6 +178,7 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { }, { event: &signal.Signal{ + Base: b, ClosePrice: decimal.NewFromInt(98), Direction: order.DoNothing, }, @@ -184,6 +186,7 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { }, { event: &signal.Signal{ + Base: b, ClosePrice: decimal.NewFromInt(1), Direction: order.DoNothing, }, @@ -191,6 +194,7 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { }, { event: &signal.Signal{ + Base: b, ClosePrice: decimal.NewFromInt(2), Direction: order.DoNothing, }, @@ -198,6 +202,7 @@ func TestSelectTopAndBottomPerformers(t *testing.T) { }, { event: &signal.Signal{ + Base: b, ClosePrice: decimal.NewFromInt(50), Direction: order.DoNothing, }, diff --git a/backtester/eventtypes/event/event.go b/backtester/eventtypes/event/event.go index 3e8e7b8ffcf..95946d41c64 100644 --- a/backtester/eventtypes/event/event.go +++ b/backtester/eventtypes/event/event.go @@ -57,7 +57,6 @@ func (b *Base) GetInterval() kline.Interval { // AppendReason adds reasoning for a decision being made func (b *Base) AppendReason(y string) { - b.Reason += y + ". " b.Reasons = append(b.Reasons, y) } @@ -65,13 +64,12 @@ func (b *Base) AppendReason(y string) { // but with formatting func (b *Base) AppendReasonf(y string, addons ...interface{}) { y = fmt.Sprintf(y, addons...) - b.Reason += y + ". " b.Reasons = append(b.Reasons, y) } -// GetReason returns the why -func (b *Base) GetReason() string { - return b.Reason +// GetConcatReasons returns the why +func (b *Base) GetConcatReasons() string { + return strings.Join(b.Reasons, ". ") } // GetReasons returns each individual reason @@ -79,6 +77,6 @@ func (b *Base) GetReasons() []string { return b.Reasons } -func (b *Base) GetBase() Base { - return *b +func (b *Base) GetBase() *Base { + return b } diff --git a/backtester/eventtypes/event/event_test.go b/backtester/eventtypes/event/event_test.go index c393eb9a292..c3dea563096 100644 --- a/backtester/eventtypes/event/event_test.go +++ b/backtester/eventtypes/event/event_test.go @@ -10,22 +10,22 @@ import ( gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" ) -func TestEvent_GetReason(t *testing.T) { +func TestGetConcatReasons(t *testing.T) { t.Parallel() e := &Base{} e.AppendReason("test") - y := e.GetReason() + y := e.GetConcatReasons() if !strings.Contains(y, "test") { t.Error("expected test") } e.AppendReason("test") - y = e.GetReason() + y = e.GetConcatReasons() if y != "test. test" { t.Error("expected 'test. test'") } } -func TestEvent_GetReasons(t *testing.T) { +func TestGetReasons(t *testing.T) { t.Parallel() e := &Base{} e.AppendReason("test") @@ -40,7 +40,7 @@ func TestEvent_GetReasons(t *testing.T) { } } -func TestEvent_GetAssetType(t *testing.T) { +func TestGetAssetType(t *testing.T) { t.Parallel() e := &Base{ AssetType: asset.Spot, @@ -50,7 +50,7 @@ func TestEvent_GetAssetType(t *testing.T) { } } -func TestEvent_GetExchange(t *testing.T) { +func TestGetExchange(t *testing.T) { t.Parallel() e := &Base{ Exchange: "test", @@ -60,7 +60,7 @@ func TestEvent_GetExchange(t *testing.T) { } } -func TestEvent_GetInterval(t *testing.T) { +func TestGetInterval(t *testing.T) { t.Parallel() e := &Base{ Interval: gctkline.OneMin, @@ -70,7 +70,7 @@ func TestEvent_GetInterval(t *testing.T) { } } -func TestEvent_GetTime(t *testing.T) { +func TestGetTime(t *testing.T) { t.Parallel() tt := time.Now() e := &Base{ @@ -82,7 +82,7 @@ func TestEvent_GetTime(t *testing.T) { } } -func TestEvent_IsEvent(t *testing.T) { +func TestIsEvent(t *testing.T) { t.Parallel() e := &Base{} if y := e.IsEvent(); !y { @@ -90,7 +90,7 @@ func TestEvent_IsEvent(t *testing.T) { } } -func TestEvent_Pair(t *testing.T) { +func TestPair(t *testing.T) { t.Parallel() e := &Base{ CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), @@ -126,11 +126,31 @@ func TestAppendReasonf(t *testing.T) { t.Parallel() b := Base{} b.AppendReasonf("%v", "hello moto") - if b.Reason != "hello moto" { - t.Errorf("epected hello moto, received '%v'", b.Reason) + if b.GetConcatReasons() != "hello moto" { + t.Errorf("expected hello moto, received '%v'", b.GetConcatReasons()) } b.AppendReasonf("%v %v", "hello", "moto") - if b.Reason != "hello moto. hello moto" { - t.Errorf("epected 'hello moto. hello moto', received '%v'", b.Reason) + if b.GetConcatReasons() != "hello moto. hello moto" { + t.Errorf("expected 'hello moto. hello moto', received '%v'", b.GetConcatReasons()) + } +} + +func TestGetBase(t *testing.T) { + t.Parallel() + b1 := &Base{ + Exchange: "hello", + } + if b1.Exchange != b1.GetBase().Exchange { + t.Errorf("expected '%v' received '%v'", b1.Exchange, b1.GetBase().Exchange) + } +} + +func TestGetUnderlyingPair(t *testing.T) { + t.Parallel() + b1 := &Base{ + UnderlyingPair: currency.NewPair(currency.BTC, currency.USDT), + } + if !b1.UnderlyingPair.Equal(b1.GetUnderlyingPair()) { + t.Errorf("expected '%v' received '%v'", b1.UnderlyingPair, b1.GetUnderlyingPair()) } } diff --git a/backtester/eventtypes/event/event_types.go b/backtester/eventtypes/event/event_types.go index 76c757e1ab4..1d6b4806cc5 100644 --- a/backtester/eventtypes/event/event_types.go +++ b/backtester/eventtypes/event/event_types.go @@ -19,6 +19,5 @@ type Base struct { CurrencyPair currency.Pair `json:"pair"` UnderlyingPair currency.Pair `json:"underlying"` AssetType asset.Item `json:"asset"` - Reason string `json:"reason"` Reasons []string `json:"reasons"` } diff --git a/backtester/eventtypes/fill/fill_types.go b/backtester/eventtypes/fill/fill_types.go index ca73ce8b909..15a81cd92e5 100644 --- a/backtester/eventtypes/fill/fill_types.go +++ b/backtester/eventtypes/fill/fill_types.go @@ -10,7 +10,7 @@ import ( // Fill is an event that details the events from placing an order type Fill struct { - event.Base + *event.Base Direction order.Side `json:"side"` Amount decimal.Decimal `json:"amount"` ClosePrice decimal.Decimal `json:"close-price"` diff --git a/backtester/eventtypes/kline/kline_test.go b/backtester/eventtypes/kline/kline_test.go index d2f7d2deca7..c3418db893b 100644 --- a/backtester/eventtypes/kline/kline_test.go +++ b/backtester/eventtypes/kline/kline_test.go @@ -51,7 +51,7 @@ func TestOpen(t *testing.T) { func TestGetUnderlyingPair(t *testing.T) { t.Parallel() k := Kline{ - Base: event.Base{ + Base: &event.Base{ UnderlyingPair: currency.NewPair(currency.USD, currency.DOGE), }, } diff --git a/backtester/eventtypes/kline/kline_types.go b/backtester/eventtypes/kline/kline_types.go index 71121e5b080..8fd8080d015 100644 --- a/backtester/eventtypes/kline/kline_types.go +++ b/backtester/eventtypes/kline/kline_types.go @@ -8,7 +8,7 @@ import ( // Kline holds kline data and an event to be processed as // a common.DataEventHandler type type Kline struct { - event.Base + *event.Base Open decimal.Decimal Close decimal.Decimal Low decimal.Decimal diff --git a/backtester/eventtypes/order/order_test.go b/backtester/eventtypes/order/order_test.go index 705b8956dcb..bda582765b1 100644 --- a/backtester/eventtypes/order/order_test.go +++ b/backtester/eventtypes/order/order_test.go @@ -43,7 +43,7 @@ func TestSetAmount(t *testing.T) { func TestIsEmpty(t *testing.T) { t.Parallel() o := Order{ - Base: event.Base{ + Base: &event.Base{ CurrencyPair: currency.NewPair(currency.BTC, currency.USDT), }, } @@ -132,7 +132,7 @@ func TestPair(t *testing.T) { t.Parallel() cp := currency.NewPair(currency.BTC, currency.USDT) k := Order{ - Base: event.Base{ + Base: &event.Base{ CurrencyPair: cp, }, } diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index e3c8df668da..e520f0b6752 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -10,7 +10,7 @@ import ( // Order contains all details for an order event type Order struct { - event.Base + *event.Base ID string Direction order.Side Status order.Status diff --git a/backtester/eventtypes/signal/signal_test.go b/backtester/eventtypes/signal/signal_test.go index 61e547a6c5f..ca0cc5b62e8 100644 --- a/backtester/eventtypes/signal/signal_test.go +++ b/backtester/eventtypes/signal/signal_test.go @@ -81,7 +81,7 @@ func TestSetAmount(t *testing.T) { func TestGetUnderlyingPair(t *testing.T) { t.Parallel() s := Signal{ - Base: event.Base{ + Base: &event.Base{ UnderlyingPair: currency.NewPair(currency.USD, currency.DOGE), }, } @@ -93,7 +93,7 @@ func TestGetUnderlyingPair(t *testing.T) { func TestPair(t *testing.T) { t.Parallel() s := Signal{ - Base: event.Base{ + Base: &event.Base{ CurrencyPair: currency.NewPair(currency.USD, currency.DOGE), }, } @@ -142,3 +142,15 @@ func TestIsNil(t *testing.T) { t.Error("expected true") } } + +func TestMatchOrderAmount(t *testing.T) { + t.Parallel() + s := &Signal{} + if s.MatchOrderAmount() { + t.Error("expected false") + } + s.MatchesOrderAmount = true + if !s.MatchOrderAmount() { + t.Error("expected true") + } +} diff --git a/backtester/eventtypes/signal/signal_types.go b/backtester/eventtypes/signal/signal_types.go index f0a120b1d50..4235dfd0ea3 100644 --- a/backtester/eventtypes/signal/signal_types.go +++ b/backtester/eventtypes/signal/signal_types.go @@ -28,7 +28,7 @@ type Event interface { // Signal contains everything needed for a strategy to raise a signal event type Signal struct { - event.Base + *event.Base OpenPrice decimal.Decimal HighPrice decimal.Decimal LowPrice decimal.Decimal diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 4c0bc207edb..ba0d2601fd1 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -84,9 +84,19 @@ func CreateItem(exch string, a asset.Item, ci currency.Code, initialFunds, trans // LinkCollateralCurrency links an item to an existing currency code // for collateral purposes func (f *FundManager) LinkCollateralCurrency(item *Item, code currency.Code) error { + if item == nil { + return fmt.Errorf("%w missing item", common.ErrNilArguments) + } + if code.IsEmpty() { + return fmt.Errorf("%w unset currency", common.ErrNilArguments) + } if !item.asset.IsFutures() { return errNotFutures } + if item.pairedWith != nil { + return fmt.Errorf("%w item already paired with %v", ErrAlreadyExists, item.pairedWith.currency) + } + for i := range f.items { if f.items[i].currency.Equal(code) && f.items[i].asset == item.asset { item.pairedWith = f.items[i] diff --git a/backtester/funding/funding_test.go b/backtester/funding/funding_test.go index e410af7e8e2..533eb733786 100644 --- a/backtester/funding/funding_test.go +++ b/backtester/funding/funding_test.go @@ -39,10 +39,13 @@ func (f *fakeEvent) Pair() currency.Pair { return pair } func (f *fakeEvent) GetExchange() string { return exchName } func (f *fakeEvent) GetInterval() gctkline.Interval { return gctkline.OneMin } func (f *fakeEvent) GetAssetType() asset.Item { return asset.Spot } -func (f *fakeEvent) GetReason() string { return "" } func (f *fakeEvent) AppendReason(string) {} func (f *fakeEvent) GetClosePrice() decimal.Decimal { return elite } func (f *fakeEvent) AppendReasonf(s string, i ...interface{}) {} +func (f *fakeEvent) GetBase() *event.Base { return &event.Base{} } +func (f *fakeEvent) GetUnderlyingPair() currency.Pair { return pair } +func (f *fakeEvent) GetConcatReasons() string { return "" } +func (f *fakeEvent) GetReasons() []string { return nil } func TestSetupFundingManager(t *testing.T) { t.Parallel() @@ -619,7 +622,7 @@ func TestFundingLiquidate(t *testing.T) { }) f.Liquidate(&signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: "test", AssetType: asset.Spot, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), @@ -641,7 +644,7 @@ func TestHasExchangeBeenLiquidated(t *testing.T) { available: decimal.NewFromInt(1337), }) ev := &signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: "test", AssetType: asset.Spot, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), @@ -766,7 +769,7 @@ func TestUpdateCollateral(t *testing.T) { } ev := &signal.Signal{ - Base: event.Base{ + Base: &event.Base{ Exchange: "ftx", AssetType: asset.Futures, CurrencyPair: currency.NewPair(currency.BTC, currency.USD), @@ -814,3 +817,51 @@ func TestUpdateCollateral(t *testing.T) { t.Errorf("recevied '%v' expected '%v'", err, expectedError) } } + +func TestCreateFuturesCurrencyCode(t *testing.T) { + t.Parallel() + if result := CreateFuturesCurrencyCode(currency.BTC, currency.USDT); result != currency.NewCode("BTC-USDT") { + t.Errorf("received '%v', expected '%v'", result, "BTC-USDT") + } +} + +func TestLinkCollateralCurrency(t *testing.T) { + t.Parallel() + f := FundManager{} + err := f.LinkCollateralCurrency(nil, currency.EMPTYCODE) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v', expected '%v'", err, common.ErrNilArguments) + } + + item := &Item{} + err = f.LinkCollateralCurrency(item, currency.EMPTYCODE) + if !errors.Is(err, common.ErrNilArguments) { + t.Errorf("received '%v', expected '%v'", err, common.ErrNilArguments) + } + + err = f.LinkCollateralCurrency(item, currency.BTC) + if !errors.Is(err, errNotFutures) { + t.Errorf("received '%v', expected '%v'", err, errNotFutures) + } + + item.asset = asset.Futures + err = f.LinkCollateralCurrency(item, currency.BTC) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } + if !item.pairedWith.currency.Equal(currency.BTC) { + t.Errorf("received '%v', expected '%v'", currency.BTC, item.pairedWith.currency) + } + + err = f.LinkCollateralCurrency(item, currency.LTC) + if !errors.Is(err, ErrAlreadyExists) { + t.Errorf("received '%v', expected '%v'", err, ErrAlreadyExists) + } + + f.items = append(f.items, item.pairedWith) + item.pairedWith = nil + err = f.LinkCollateralCurrency(item, currency.BTC) + if !errors.Is(err, nil) { + t.Errorf("received '%v', expected '%v'", err, nil) + } +} diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index fd08eb71fae..82c55094539 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -108,8 +108,8 @@ type DetailedCandle struct { type linkCurrencyDiff struct { FuturesPair currency.Pair SpotPair currency.Pair - FuturesEvents []*statistics.DataAtOffset //TODO investigate if that did anything - SpotEvents []*statistics.DataAtOffset + FuturesEvents []statistics.DataAtOffset + SpotEvents []statistics.DataAtOffset DiffPercent []decimal.Decimal } diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index ee0585d2df7..2e02c0b20cc 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -2837,7 +2837,7 @@ func TestFetchSpotExchangeLimits(t *testing.T) { t.Parallel() limits, err := b.FetchSpotExchangeLimits(context.Background()) if !errors.Is(err, nil) { - t.Errorf("received '%v', epected '%v'", err, nil) + t.Errorf("received '%v', expected '%v'", err, nil) } if len(limits) == 0 { t.Error("expected a response") diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 4535b575d97..b3c3ce3befb 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1850,7 +1850,7 @@ func TestScaleCollateral(t *testing.T) { CalculateOffline: true, }) if !errors.Is(err, order.ErrUSDValueRequired) { - t.Errorf("received '%v' exepected '%v'", err, order.ErrUSDValueRequired) + t.Errorf("received '%v' expected '%v'", err, order.ErrUSDValueRequired) } _, err = f.ScaleCollateral( From dfdc81a4cb3b713e338811b24765d18e173040fc Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 12 May 2022 11:52:17 +1000 Subject: [PATCH 147/171] Fix logo colouring --- backtester/common/common.go | 25 +++++++++++++++++++++++++ backtester/common/common_test.go | 11 +++++++++++ backtester/common/common_types.go | 24 ------------------------ backtester/main.go | 6 +----- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/backtester/common/common.go b/backtester/common/common.go index af94954a42d..88e34ded67d 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -123,3 +123,28 @@ func PurgeColours() { ColourDarkGrey = "" ColourError = "" } + +// Logo returns the logo +func Logo() string { + sb := strings.Builder{} + sb.WriteString(" \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@@@@@@@@@@ \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + ",,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@" + ColourGrey + "(,,,,,,,, " + ColourGrey + ",," + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourGrey + ",," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, #,,,,,,,,,,,,,,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourGrey + ",,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,,,,,,,,,,,,,," + ColourGreen + "%%%%%%%" + ColourWhite + " \n") + sb.WriteString(" " + ColourGrey + ",,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,," + ColourGreen + "%%%%%" + ColourGrey + " ,,,,,," + ColourGrey + "%" + ColourGreen + "%%%%%%" + ColourWhite + " \n") + sb.WriteString(" " + ColourGrey + ",,,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,," + ColourGreen + "%%%%%%%%%%%%%%%%%%" + ColourGrey + "#" + ColourGreen + "%%" + ColourGrey + " \n") + sb.WriteString(" " + ColourGrey + ",,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,," + ColourGreen + "%%%" + ColourGrey + " ,,,,," + ColourGreen + "%%%%%%%%" + ColourGrey + ",,,,, \n") + sb.WriteString(" " + ColourGrey + ",,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourGreen + "%%" + ColourGrey + ",, ,,,,,,," + ColourWhite + "@" + ColourGreen + "*%%," + ColourWhite + "@" + ColourGrey + ",,,,,, \n") + sb.WriteString(" " + ColourGrey + "*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourGrey + ",,,,," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + "#,,,,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + "*,,,," + ColourWhite + " \n") + sb.WriteString(" " + ColourWhite + "@@@@@@@@@@@@@@@@" + ColourDefault + " \n") + sb.WriteString(ASCIILogo) + return sb.String() +} diff --git a/backtester/common/common_test.go b/backtester/common/common_test.go index 60aca1b9f26..0724cdc1ec1 100644 --- a/backtester/common/common_test.go +++ b/backtester/common/common_test.go @@ -104,6 +104,17 @@ func TestFitStringToLimit(t *testing.T) { } } +func TestLogo(t *testing.T) { + colourLogo := Logo() + if colourLogo == "" { + t.Error("expected a logo") + } + PurgeColours() + if len(colourLogo) == len(Logo()) { + t.Error("expected logo with colours removed") + } +} + func TestPurgeColours(t *testing.T) { PurgeColours() if ColourSuccess != "" { diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 0e5409bd1f9..09afc7658fd 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -104,30 +104,6 @@ var ( ColourError = "\033[38;5;196m" ) -var ( - // LogoLines contains the lovely GCT logo - LogoLines = []string{ - " ", - " " + ColourWhite + "@@@@@@@@@@@@@@@@@ ", - " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + ",,,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,,,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@" + ColourGrey + "(,,,,,,,, " + ColourGrey + ",," + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " ", - " " + ColourGrey + ",," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, #,,,,,,,,,,,,,,,,,," + ColourWhite + " ", - " " + ColourGrey + ",,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,,,,,,,,,,,,,," + ColourGreen + "%%%%%%%" + ColourWhite + " ", - " " + ColourGrey + ",,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,,,,," + ColourGreen + "%%%%%" + ColourGrey + " ,,,,,," + ColourGrey + "%" + ColourGreen + "%%%%%%" + ColourWhite + " ", - " " + ColourGrey + ",,,,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,,,," + ColourGreen + "%%%%%%%%%%%%%%%%%%" + ColourGrey + "#" + ColourGreen + "%%" + ColourGrey + " ", - " " + ColourGrey + ",,,,,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,," + ColourGreen + "%%%" + ColourGrey + " ,,,,," + ColourGreen + "%%%%%%%%" + ColourGrey + ",,,,, ", - " " + ColourGrey + ",,,*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourGreen + "%%" + ColourGrey + ",, ,,,,,,," + ColourWhite + "@" + ColourGreen + "*%%," + ColourWhite + "@" + ColourGrey + ",,,,,, ", - " " + ColourGrey + "*" + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourGrey + ",,,,," + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " , ", - " " + ColourWhite + "@@@@@@" + ColourGrey + ",,,,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@@@" + ColourGrey + ",,,,,,, " + ColourWhite + "@@@@@@@" + ColourGrey + ",,,,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@@@@" + ColourGrey + ",,,, " + ColourWhite + "@@@@@@@@@" + ColourGrey + "#,,,,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@@@@@@@@@@@@@@@@@@ " + ColourGrey + "*,,,," + ColourWhite + " ", - " " + ColourWhite + "@@@@@@@@@@@@@@@@" + ColourDefault + " ", - } -) - // ASCIILogo is a sweet logo that is optionally printed to the command line window const ASCIILogo = ` ______ ______ __ ______ __ diff --git a/backtester/main.go b/backtester/main.go index 982ebf3481f..ae1c9491669 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -77,7 +77,6 @@ func main() { true, "displays logging subheader to track where activity originates") flag.Parse() - if !colourOutput { common.PurgeColours() } @@ -107,10 +106,7 @@ func main() { os.Exit(1) } if printLogo { - for i := range common.LogoLines { - fmt.Println(common.LogoLines[i]) - } - fmt.Print(common.ASCIILogo) + fmt.Println(common.Logo()) } err = cfg.Validate() From 6827d93ee503150f665936e80bd2031844255e16 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 13 May 2022 11:35:01 +1000 Subject: [PATCH 148/171] Fixes niteroonies --- .../statistics/fundingstatistics.go | 4 +- .../eventhandlers/statistics/printresults.go | 4 +- .../statistics/statistics_types.go | 2 - .../ftxcashandcarry/ftxcashandcarry.go | 2 +- backtester/funding/funding.go | 102 +++++++++--------- backtester/funding/funding_types.go | 6 +- 6 files changed, 59 insertions(+), 61 deletions(-) diff --git a/backtester/eventhandlers/statistics/fundingstatistics.go b/backtester/eventhandlers/statistics/fundingstatistics.go index 09c6ecabaee..37dd2dfbb1b 100644 --- a/backtester/eventhandlers/statistics/fundingstatistics.go +++ b/backtester/eventhandlers/statistics/fundingstatistics.go @@ -89,9 +89,7 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str usdStats.HoldingValues[0].Value).Mul( decimal.NewFromInt(100)) } - usdStats.InitialHoldingValue = usdStats.HoldingValues[0] - usdStats.FinalHoldingValue = usdStats.HoldingValues[len(usdStats.HoldingValues)-1] - usdStats.HoldingValueDifference = usdStats.FinalHoldingValue.Value.Sub(usdStats.InitialHoldingValue.Value).Div(usdStats.InitialHoldingValue.Value).Mul(decimal.NewFromInt(100)) + usdStats.HoldingValueDifference = report.FinalFunds.Sub(report.InitialFunds).Div(report.InitialFunds).Mul(decimal.NewFromInt(100)) riskFreeRatePerCandle := usdStats.RiskFreeRate.Div(decimal.NewFromFloat(interval.IntervalsPerYear())) returnsPerCandle := make([]decimal.Decimal, len(usdStats.HoldingValues)) diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index fb48116ed4a..c21660b6929 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -344,8 +344,8 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error { log.Info(common.FundingStatistics, common.ColourH2+"------------------USD Tracking Totals------------------------"+common.ColourDefault) sep := "USD Tracking Total |\t" - log.Infof(common.FundingStatistics, "%s Initial value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.InitialHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.InitialHoldingValue.Time) - log.Infof(common.FundingStatistics, "%s Final value: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.FinalHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.FinalHoldingValue.Time) + log.Infof(common.FundingStatistics, "%s Initial value: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.InitialFunds, 8, ".", ",")) + log.Infof(common.FundingStatistics, "%s Final value: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.FinalFunds, 8, ".", ",")) log.Infof(common.FundingStatistics, "%s Benchmark Market Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.BenchmarkMarketMovement, 8, ".", ",")) log.Infof(common.FundingStatistics, "%s Strategy Movement: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.StrategyMovement, 8, ".", ",")) log.Infof(common.FundingStatistics, "%s Did strategy make a profit: %v", sep, f.TotalUSDStatistics.DidStrategyMakeProfit) diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index 0181c6d3326..b72e9ebb0ce 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -250,8 +250,6 @@ type FundingItemStatistics struct { // TotalFundingStatistics holds values for overall statistics for funding items type TotalFundingStatistics struct { HoldingValues []ValueAtTime - InitialHoldingValue ValueAtTime - FinalHoldingValue ValueAtTime HighestHoldingValue ValueAtTime LowestHoldingValue ValueAtTime BenchmarkMarketMovement decimal.Decimal diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index 0f9e64fcb3d..cc658a4f921 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -85,7 +85,7 @@ func (s *Strategy) OnSimultaneousSignals(d []data.Handler, f funding.IFundingTra diffBetweenFuturesSpot := fp.Sub(sp).Div(sp).Mul(decimal.NewFromInt(100)) futuresSignal.AppendReasonf("Futures Spot Difference: %v%%", diffBetweenFuturesSpot) if len(pos) > 0 && pos[len(pos)-1].Status == order.Open { - futuresSignal.AppendReasonf("Unrealised PNL: %v %v", pos[len(pos)-1].UnrealisedPNL, pos[len(pos)-1].Underlying) + futuresSignal.AppendReasonf("Unrealised PNL: %v %v", pos[len(pos)-1].UnrealisedPNL, pos[len(pos)-1].CollateralCurrency) } if f.HasExchangeBeenLiquidated(&spotSignal) || f.HasExchangeBeenLiquidated(&futuresSignal) { spotSignal.AppendReason("cannot transact, has been liquidated") diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index ba0d2601fd1..5edc0cdc326 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -124,12 +124,6 @@ func (f *FundManager) CreateSnapshot(t time.Time) { for i := range f.items { if f.items[i].snapshot == nil { f.items[i].snapshot = make(map[int64]ItemSnapshot) - if f.items[i].isCollateral { - f.items[i].initialFunds = f.items[i].available - } - } else if _, ok := f.items[i].snapshot[t.UnixNano()]; !ok { - f.items[i].snapshot[t.UnixNano()] = ItemSnapshot{} - // todo investigate this } iss := ItemSnapshot{ @@ -305,55 +299,57 @@ func (f *FundManager) GenerateReport() *Report { DisableUSDTracking: f.disableUSDTracking, } items := make([]ReportItem, len(f.items)) - for i := range f.items { + for x := range f.items { item := ReportItem{ - Exchange: f.items[i].exchange, - Asset: f.items[i].asset, - Currency: f.items[i].currency, - InitialFunds: f.items[i].initialFunds, - TransferFee: f.items[i].transferFee, - FinalFunds: f.items[i].available, - IsCollateral: f.items[i].isCollateral, + Exchange: f.items[x].exchange, + Asset: f.items[x].asset, + Currency: f.items[x].currency, + InitialFunds: f.items[x].initialFunds, + TransferFee: f.items[x].transferFee, + FinalFunds: f.items[x].available, + IsCollateral: f.items[x].isCollateral, } if !f.disableUSDTracking && - f.items[i].trackingCandles != nil { - usdStream := f.items[i].trackingCandles.GetStream() - item.USDInitialFunds = f.items[i].initialFunds.Mul(usdStream[0].GetClosePrice()) - item.USDFinalFunds = f.items[i].available.Mul(usdStream[len(usdStream)-1].GetClosePrice()) + f.items[x].trackingCandles != nil { + usdStream := f.items[x].trackingCandles.GetStream() + item.USDInitialFunds = f.items[x].initialFunds.Mul(usdStream[0].GetClosePrice()) + item.USDFinalFunds = f.items[x].available.Mul(usdStream[len(usdStream)-1].GetClosePrice()) item.USDInitialCostForOne = usdStream[0].GetClosePrice() item.USDFinalCostForOne = usdStream[len(usdStream)-1].GetClosePrice() - item.USDPairCandle = f.items[i].trackingCandles + item.USDPairCandle = f.items[x].trackingCandles } + // create a breakdown of USD values and currency contributions over the span of run var pricingOverTime []ItemSnapshot snaps: - for _, v := range f.items[i].snapshot { - pricingOverTime = append(pricingOverTime, v) - if !f.items[i].asset.IsFutures() && !f.disableUSDTracking { - for j := range report.USDTotalsOverTime { - if report.USDTotalsOverTime[j].Time.Equal(v.Time) { - report.USDTotalsOverTime[j].USDValue = report.USDTotalsOverTime[j].USDValue.Add(v.USDValue) - report.USDTotalsOverTime[j].Breakdown = append(report.USDTotalsOverTime[j].Breakdown, CurrencyContribution{ - Currency: f.items[i].currency, - USD: v.USDValue, - }) - continue snaps - } else { - continue - } + for _, snapshot := range f.items[x].snapshot { + pricingOverTime = append(pricingOverTime, snapshot) + if f.items[x].asset.IsFutures() || f.disableUSDTracking { + // futures contracts / collateral does not contribute to USD value + // no USD tracking means no USD values to breakdown + break + } + for y := range report.USDTotalsOverTime { + if report.USDTotalsOverTime[y].Time.Equal(snapshot.Time) { + report.USDTotalsOverTime[y].USDValue = report.USDTotalsOverTime[y].USDValue.Add(snapshot.USDValue) + report.USDTotalsOverTime[y].Breakdown = append(report.USDTotalsOverTime[y].Breakdown, CurrencyContribution{ + Currency: f.items[x].currency, + USDContribution: snapshot.USDValue, + }) + continue snaps } - report.USDTotalsOverTime = append(report.USDTotalsOverTime, ItemSnapshot{ - Time: v.Time, - USDValue: v.USDValue, - Breakdown: []CurrencyContribution{ - { - Currency: f.items[i].currency, - USD: v.USDValue, - }, - }, - }) } + report.USDTotalsOverTime = append(report.USDTotalsOverTime, ItemSnapshot{ + Time: snapshot.Time, + USDValue: snapshot.USDValue, + Breakdown: []CurrencyContribution{ + { + Currency: f.items[x].currency, + USDContribution: snapshot.USDValue, + }, + }, + }) } sort.Slice(pricingOverTime, func(i, j int) bool { @@ -361,21 +357,25 @@ func (f *FundManager) GenerateReport() *Report { }) item.Snapshots = pricingOverTime - if f.items[i].initialFunds.IsZero() { + if f.items[x].initialFunds.IsZero() { item.ShowInfinite = true } else { - item.Difference = f.items[i].available.Sub(f.items[i].initialFunds).Div(f.items[i].initialFunds).Mul(decimal.NewFromInt(100)) + item.Difference = f.items[x].available.Sub(f.items[x].initialFunds).Div(f.items[x].initialFunds).Mul(decimal.NewFromInt(100)) } - if f.items[i].pairedWith != nil { - item.PairedWith = f.items[i].pairedWith.currency + if f.items[x].pairedWith != nil { + item.PairedWith = f.items[x].pairedWith.currency } + report.InitialFunds = report.InitialFunds.Add(item.USDInitialFunds) - items[i] = item + items[x] = item } - sort.Slice(report.USDTotalsOverTime, func(i, j int) bool { - return report.USDTotalsOverTime[i].Time.Before(report.USDTotalsOverTime[j].Time) - }) + if len(report.USDTotalsOverTime) > 0 { + report.FinalFunds = report.USDTotalsOverTime[len(report.USDTotalsOverTime)-1].USDValue + sort.Slice(report.USDTotalsOverTime, func(i, j int) bool { + return report.USDTotalsOverTime[i].Time.Before(report.USDTotalsOverTime[j].Time) + }) + } report.Items = items return &report diff --git a/backtester/funding/funding_types.go b/backtester/funding/funding_types.go index 0bc9fb9e397..e30245846cb 100644 --- a/backtester/funding/funding_types.go +++ b/backtester/funding/funding_types.go @@ -170,6 +170,8 @@ type Report struct { UsingExchangeLevelFunding bool Items []ReportItem USDTotalsOverTime []ItemSnapshot + InitialFunds decimal.Decimal + FinalFunds decimal.Decimal } // ReportItem holds reporting fields @@ -205,6 +207,6 @@ type ItemSnapshot struct { // CurrencyContribution helps breakdown how a USD value // determines its number type CurrencyContribution struct { - Currency currency.Code - USD decimal.Decimal + Currency currency.Code + USDContribution decimal.Decimal } From 604b033af50dd05640de0418220d574ba5de3d58 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 13 May 2022 12:38:46 +1000 Subject: [PATCH 149/171] Fix report --- backtester/eventhandlers/portfolio/portfolio.go | 1 + backtester/report/tpl.gohtml | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 2538e80cbf0..4b0fedc7134 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -117,6 +117,7 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi return cannotPurchase(ev, o) } sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) + sizedOrder.SetDirection(side) if ev.GetDirection() == gctorder.ClosePosition { sizedOrder.ClosingPosition = true diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 67aadd0a1a0..5dab4a69a1d 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -362,11 +362,11 @@ Initial Total Funds in USD - ${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.TotalUSDStatistics.InitialHoldingValue.Value}} + ${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.Report.InitialFunds}} Final Total Funds in USD - ${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.TotalUSDStatistics.FinalHoldingValue.Value}} + ${{ $.Prettify.Decimal2 .Statistics.FundingStatistics.Report.FinalFunds}} Difference @@ -1559,11 +1559,11 @@ Initial Total USD Value - ${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.InitialHoldingValue.Value}} + ${{$.Prettify.Decimal8 .Statistics.FundingStatistics.Report.InitialFunds}} Final Total USD Value - ${{$.Prettify.Decimal8 .Statistics.FundingStatistics.TotalUSDStatistics.FinalHoldingValue.Value}} + ${{$.Prettify.Decimal8 .Statistics.FundingStatistics.Report.FinalFunds}} Difference From d9c272cc124e50fca5af38f2a054c5c2be873e0c Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 13 May 2022 16:43:43 +1000 Subject: [PATCH 150/171] BAD COMMIT --- backtester/eventhandlers/exchange/exchange.go | 11 +++++-- .../eventhandlers/portfolio/portfolio.go | 31 ++++++++++--------- .../eventhandlers/portfolio/size/size.go | 23 +++++++++----- backtester/funding/collateralpair.go | 2 ++ 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 22cbea9f514..5b60acfdc10 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -19,6 +19,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/log" ) // Reset returns the exchange to initial settings @@ -43,9 +44,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if o.GetDirection() == gctorder.DoNothing { return f, ErrDoNothing } - if o.GetAssetType().IsFutures() && !o.IsClosingPosition() { - f.Amount = o.GetAllocatedFunds() - } + eventFunds := o.GetAllocatedFunds() cs, err := e.GetCurrencySettings(o.GetExchange(), o.GetAssetType(), o.Pair()) if err != nil { @@ -127,16 +126,20 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.ExchangeFee = calculateExchangeFee(adjustedPrice, amount, cs.TakerFee) } + log.Debugf(log.ExchangeSys, "amount %v", amount) portfolioLimitedAmount := reduceAmountToFitPortfolioLimit(adjustedPrice, amount, eventFunds, f.GetDirection()) if !portfolioLimitedAmount.Equal(amount) { f.AppendReasonf("Order size shrunk from %v to %v to remain within portfolio limits", amount, portfolioLimitedAmount) } + log.Debugf(log.ExchangeSys, "portfolioLimitedAmount %v", portfolioLimitedAmount) limitReducedAmount := portfolioLimitedAmount + log.Debugf(log.ExchangeSys, "limitReducedAmount %v", limitReducedAmount) if cs.CanUseExchangeLimits { // Conforms the amount to the exchange order defined step amount // reducing it when needed limitReducedAmount = cs.Limits.ConformToDecimalAmount(portfolioLimitedAmount) + log.Debugf(log.ExchangeSys, "limitReducedAmount %v", limitReducedAmount) if !limitReducedAmount.Equal(portfolioLimitedAmount) { f.AppendReasonf("Order size shrunk from %v to %v to remain within exchange step amount limits", portfolioLimitedAmount, @@ -147,7 +150,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if err != nil { return f, err } + log.Debugf(log.ExchangeSys, "limitReducedAmount %v", limitReducedAmount) f.ExchangeFee = calculateExchangeFee(adjustedPrice, limitReducedAmount, cs.ExchangeFee) + log.Debugf(log.ExchangeSys, "price %v amount %v, fee %v", adjustedPrice, limitReducedAmount, f.GetExchangeFee()) orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) if err != nil { diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 4b0fedc7134..e2701373e7e 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -116,8 +116,10 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi if sizingFunds.LessThanOrEqual(decimal.Zero) { return cannotPurchase(ev, o) } - sizedOrder := p.sizeOrder(ev, cs, o, sizingFunds, funds) - + sizedOrder, err := p.sizeOrder(ev, cs, o, sizingFunds, funds) + if err != nil { + return sizedOrder, err + } sizedOrder.SetDirection(side) if ev.GetDirection() == gctorder.ClosePosition { sizedOrder.ClosingPosition = true @@ -179,7 +181,7 @@ func (p *Portfolio) evaluateOrder(d common.Directioner, originalOrderSignal, ev return evaluatedOrder, nil } -func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, originalOrderSignal *order.Order, sizingFunds decimal.Decimal, funds funding.IFundReserver) *order.Order { +func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, originalOrderSignal *order.Order, sizingFunds decimal.Decimal, funds funding.IFundReserver) (*order.Order, error) { sizedOrder, estFee, err := p.sizeManager.SizeOrder(originalOrderSignal, sizingFunds, cs) if err != nil || sizedOrder.Amount.IsZero() { switch originalOrderSignal.Direction { @@ -197,36 +199,35 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi d.SetDirection(originalOrderSignal.Direction) if err != nil { originalOrderSignal.AppendReason(err.Error()) - return originalOrderSignal + return originalOrderSignal, nil } originalOrderSignal.AppendReason("sized order to 0") } switch d.GetDirection() { case gctorder.Buy, gctorder.Bid: - err = funds.Reserve(sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee), gctorder.Buy) - sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice) + sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) case gctorder.Sell, gctorder.Ask: - err = funds.Reserve(sizedOrder.Amount.Add(estFee), gctorder.Sell) - sizedOrder.AllocatedSize = sizedOrder.Amount + sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) case gctorder.Short, gctorder.Long: - err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) - sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) + sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) case gctorder.ClosePosition: if originalOrderSignal.AssetType.IsFutures() { - err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) - sizedOrder.AllocatedSize = sizedOrder.Amount.Div(sizedOrder.ClosePrice) + sizedOrder.AllocatedSize = sizedOrder.Amount.Add(estFee) } else { err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) sizedOrder.AllocatedSize = sizedOrder.Amount } + //sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) default: - err = errInvalidDirection + return nil, errInvalidDirection } + err = funds.Reserve(sizedOrder.AllocatedSize, d.GetDirection()) if err != nil { sizedOrder.Direction = gctorder.DoNothing - sizedOrder.AppendReason(err.Error()) + return sizedOrder, err } - return sizedOrder + log.Debugf(log.ExchangeSys, "price %v allocatedsize %v, fee %v", sizedOrder.ClosePrice, sizedOrder.AllocatedSize, estFee) + return sizedOrder, nil } // OnFill processes the event after an order has been placed by the exchange. Its purpose is to track holdings for future portfolio decisions. diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index c55b7a51348..d87128fd445 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -9,6 +9,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" ) // SizeOrder is responsible for ensuring that the order size is within config limits @@ -23,6 +24,7 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc if !ok { return nil, decimal.Decimal{}, fmt.Errorf("%w expected order event", common.ErrInvalidDataType) } + if fde := o.GetFillDependentEvent(); fde != nil && fde.MatchOrderAmount() { scalingInfo, err := cs.Exchange.ScaleCollateral(context.TODO(), &gctorder.CollateralCalculator{ CalculateOffline: true, @@ -63,10 +65,19 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc } func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (amount, fee decimal.Decimal, err error) { + if o.GetAmount().IsPositive() { + // when an order amount is already set + // use the pre-set amount and calculate the fee + fee = o.GetAmount().Mul(price).Mul(cs.TakerFee) + log.Debugf(log.ExchangeSys, "orderAmount %v fee %v feeRate %v price %v fullPrice %v", o.GetAmount(), fee, cs.ExchangeFee, price, price.Mul(o.GetAmount())) + return o.GetAmount(), fee, nil + } var portfolioAmount, portfolioFee decimal.Decimal switch direction { case gctorder.ClosePosition: - amount = amountAvailable + oneMFeeRate := decimal.NewFromInt(1).Sub(cs.ExchangeFee) + amount = amountAvailable.Mul(oneMFeeRate) + fee = amount.Mul(price).Mul(cs.ExchangeFee) case gctorder.Buy, gctorder.Long: // check size against currency specific settings amount, fee, err = s.calculateBuySize(price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) @@ -85,10 +96,13 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d } case gctorder.Sell, gctorder.Short: // check size against currency specific settings + log.Debugf(log.ExchangeSys, "price %v amount %v, fee %v", price, amount, cs.ExchangeFee) + log.Debugf(log.ExchangeSys, "price %v amount %v, fee %v", price, amountAvailable, cs.ExchangeFee) amount, fee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) if err != nil { return decimal.Decimal{}, decimal.Decimal{}, err } + log.Debugf(log.ExchangeSys, "amount %v, fee %v", amount, fee) // check size against portfolio specific settings portfolioAmount, portfolioFee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) if err != nil { @@ -102,13 +116,6 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d default: return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } - if o.GetAmount().IsPositive() { - setAmountSize := o.GetAmount().Mul(price) - if setAmountSize.LessThan(amount) { - amount = setAmountSize - fee = amount.Mul(price).Mul(cs.TakerFee) - } - } if amount.LessThanOrEqual(decimal.Zero) { return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) diff --git a/backtester/funding/collateralpair.go b/backtester/funding/collateralpair.go index 6b60881e55f..598ff257ecb 100644 --- a/backtester/funding/collateralpair.go +++ b/backtester/funding/collateralpair.go @@ -7,6 +7,7 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/log" ) // collateral related errors @@ -82,6 +83,7 @@ func (c *CollateralPair) ReleaseContracts(amount decimal.Decimal) error { // Reserve reserves or releases collateral based on order side func (c *CollateralPair) Reserve(amount decimal.Decimal, side gctorder.Side) error { + log.Debugf(log.ExchangeSys, "reserving amount %v", amount) switch side { case gctorder.Long, gctorder.Short: return c.collateral.Reserve(amount) From 83c7881104222e0afebe8144b4d6c9c8a6a8a109 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 16 May 2022 12:22:01 +1000 Subject: [PATCH 151/171] Fixes funding issues.Updates default fee rates.Combines cashcarry case --- backtester/config/config_test.go | 52 +++++----- ...a-api-candles-exchange-level-funding.strat | 8 +- .../dca-api-candles-multiple-currencies.strat | 8 +- ...-api-candles-simultaneous-processing.strat | 8 +- .../config/examples/dca-api-candles.strat | 4 +- .../config/examples/dca-api-trades.strat | 4 +- .../config/examples/dca-candles-live.strat | 4 +- .../config/examples/dca-csv-candles.strat | 4 +- .../config/examples/dca-csv-trades.strat | 4 +- .../examples/dca-database-candles.strat | 4 +- .../config/examples/ftx-cash-carry.strat | 8 +- .../config/examples/rsi-api-candles.strat | 8 +- .../t2b2-api-candles-exchange-funding.strat | 24 ++--- backtester/eventhandlers/exchange/exchange.go | 97 +++++++++++-------- .../eventhandlers/exchange/exchange_test.go | 48 ++++----- .../eventhandlers/portfolio/portfolio.go | 28 +++--- .../eventhandlers/portfolio/size/size.go | 21 ++-- .../eventhandlers/portfolio/size/size_test.go | 1 + .../ftxcashandcarry/ftxcashandcarry.go | 18 ++-- .../ftxcashandcarry/ftxcashandcarry_test.go | 13 ++- backtester/eventtypes/order/order.go | 2 +- backtester/eventtypes/order/order_test.go | 2 +- backtester/eventtypes/order/order_types.go | 2 +- backtester/funding/collateralpair.go | 6 +- backtester/funding/collateralpair_test.go | 10 +- backtester/funding/funding.go | 2 +- backtester/report/tpl.gohtml | 4 +- 27 files changed, 197 insertions(+), 197 deletions(-) diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 3d802ed7f75..0b550ae46c8 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -32,25 +32,25 @@ var ( startDate = time.Date(time.Now().Year()-1, 8, 1, 0, 0, 0, 0, time.Local) endDate = time.Date(time.Now().Year()-1, 12, 1, 0, 0, 0, 0, time.Local) tradeEndDate = startDate.Add(time.Hour * 72) - makerFee = decimal.NewFromFloat(0.001) - takerFee = decimal.NewFromFloat(0.002) + makerFee = decimal.NewFromFloat(0.0002) + takerFee = decimal.NewFromFloat(0.0007) minMax = MinMax{ MinimumSize: decimal.NewFromFloat(0.005), MaximumSize: decimal.NewFromInt(2), MaximumTotal: decimal.NewFromInt(40000), } - initialQuoteFunds1 *decimal.Decimal - initialQuoteFunds2 *decimal.Decimal - initialBaseFunds *decimal.Decimal + initialFunds1000000 *decimal.Decimal + initialFunds100000 *decimal.Decimal + initialFunds10 *decimal.Decimal ) func TestMain(m *testing.M) { iF1 := decimal.NewFromInt(1000000) iF2 := decimal.NewFromInt(100000) iBF := decimal.NewFromInt(10) - initialQuoteFunds1 = &iF1 - initialQuoteFunds2 = &iF2 - initialBaseFunds = &iBF + initialFunds1000000 = &iF1 + initialFunds100000 = &iF2 + initialFunds10 = &iBF os.Exit(m.Run()) } @@ -366,8 +366,8 @@ func TestPrintSettings(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds1, - InitialBaseFunds: initialQuoteFunds1, + InitialQuoteFunds: initialFunds1000000, + InitialBaseFunds: initialFunds1000000, }, BuySide: minMax, SellSide: minMax, @@ -432,8 +432,8 @@ func TestValidate(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialBaseFunds: initialBaseFunds, - InitialQuoteFunds: initialQuoteFunds2, + InitialBaseFunds: initialFunds10, + InitialQuoteFunds: initialFunds100000, }, BuySide: MinMax{ MinimumSize: decimal.NewFromInt(1), @@ -485,7 +485,7 @@ func TestGenerateConfigForDCAAPICandles(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -627,7 +627,7 @@ func TestGenerateConfigForDCAAPITrades(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -697,7 +697,7 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -710,7 +710,7 @@ func TestGenerateConfigForDCAAPICandlesMultipleCurrencies(t *testing.T) { Base: currency.ETH, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -772,7 +772,7 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds1, + InitialQuoteFunds: initialFunds1000000, }, BuySide: minMax, SellSide: minMax, @@ -785,7 +785,7 @@ func TestGenerateConfigForDCAAPICandlesSimultaneousProcessing(t *testing.T) { Base: currency.ETH, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -847,7 +847,7 @@ func TestGenerateConfigForDCALiveCandles(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -916,7 +916,7 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -929,8 +929,8 @@ func TestGenerateConfigForRSIAPICustomSettings(t *testing.T) { Base: currency.ETH, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialBaseFunds: initialBaseFunds, - InitialQuoteFunds: initialQuoteFunds1, + InitialBaseFunds: initialFunds10, + InitialQuoteFunds: initialFunds1000000, }, BuySide: minMax, SellSide: minMax, @@ -993,7 +993,7 @@ func TestGenerateConfigForDCACSVCandles(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -1054,7 +1054,7 @@ func TestGenerateConfigForDCACSVTrades(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, MakerFee: &makerFee, TakerFee: &takerFee, @@ -1109,7 +1109,7 @@ func TestGenerateConfigForDCADatabaseCandles(t *testing.T) { Base: currency.BTC, Quote: currency.USDT, SpotDetails: &SpotDetails{ - InitialQuoteFunds: initialQuoteFunds2, + InitialQuoteFunds: initialFunds100000, }, BuySide: minMax, SellSide: minMax, @@ -1309,7 +1309,7 @@ func TestGenerateFTXCashAndCarryStrategy(t *testing.T) { ExchangeName: "ftx", Asset: asset.Spot, Currency: currency.USD, - InitialFunds: *initialQuoteFunds2, + InitialFunds: *initialFunds100000, }, }, }, diff --git a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat index f20867916e0..dca2e09819f 100644 --- a/backtester/config/examples/dca-api-candles-exchange-level-funding.strat +++ b/backtester/config/examples/dca-api-candles-exchange-level-funding.strat @@ -36,8 +36,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -60,8 +60,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-api-candles-multiple-currencies.strat b/backtester/config/examples/dca-api-candles-multiple-currencies.strat index a31298a69ad..08de70e93aa 100644 --- a/backtester/config/examples/dca-api-candles-multiple-currencies.strat +++ b/backtester/config/examples/dca-api-candles-multiple-currencies.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -57,8 +57,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat index 7211b0ab7c6..662f4a5ec46 100644 --- a/backtester/config/examples/dca-api-candles-simultaneous-processing.strat +++ b/backtester/config/examples/dca-api-candles-simultaneous-processing.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -57,8 +57,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-api-candles.strat b/backtester/config/examples/dca-api-candles.strat index 647b112a447..6e93d23c865 100644 --- a/backtester/config/examples/dca-api-candles.strat +++ b/backtester/config/examples/dca-api-candles.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-api-trades.strat b/backtester/config/examples/dca-api-trades.strat index c2b75e076b1..a253a2a7bca 100644 --- a/backtester/config/examples/dca-api-trades.strat +++ b/backtester/config/examples/dca-api-trades.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": true, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-candles-live.strat b/backtester/config/examples/dca-candles-live.strat index 569b96c248c..7fe0bedefb0 100644 --- a/backtester/config/examples/dca-candles-live.strat +++ b/backtester/config/examples/dca-candles-live.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-csv-candles.strat b/backtester/config/examples/dca-csv-candles.strat index abd58518b4b..b95c3639d56 100644 --- a/backtester/config/examples/dca-csv-candles.strat +++ b/backtester/config/examples/dca-csv-candles.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-csv-trades.strat b/backtester/config/examples/dca-csv-trades.strat index e3d3b46a9e6..8bdd43dd3d7 100644 --- a/backtester/config/examples/dca-csv-trades.strat +++ b/backtester/config/examples/dca-csv-trades.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/dca-database-candles.strat b/backtester/config/examples/dca-database-candles.strat index 731ecf5d7d9..08e2b18e282 100644 --- a/backtester/config/examples/dca-database-candles.strat +++ b/backtester/config/examples/dca-database-candles.strat @@ -30,8 +30,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/ftx-cash-carry.strat b/backtester/config/examples/ftx-cash-carry.strat index 1dac4b22bfd..d820027a8a6 100644 --- a/backtester/config/examples/ftx-cash-carry.strat +++ b/backtester/config/examples/ftx-cash-carry.strat @@ -36,8 +36,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -60,8 +60,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/rsi-api-candles.strat b/backtester/config/examples/rsi-api-candles.strat index 89954c79955..2a97532e601 100644 --- a/backtester/config/examples/rsi-api-candles.strat +++ b/backtester/config/examples/rsi-api-candles.strat @@ -35,8 +35,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -63,8 +63,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat index 687944bb07d..4ba22996368 100644 --- a/backtester/config/examples/t2b2-api-candles-exchange-funding.strat +++ b/backtester/config/examples/t2b2-api-candles-exchange-funding.strat @@ -48,8 +48,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -72,8 +72,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -96,8 +96,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -120,8 +120,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -144,8 +144,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, @@ -168,8 +168,8 @@ }, "min-slippage-percent": "0", "max-slippage-percent": "0", - "maker-fee-override": "0.001", - "taker-fee-override": "0.002", + "maker-fee-override": "0.0002", + "taker-fee-override": "0.0007", "maximum-holdings-ratio": "0", "skip-candle-volume-fitting": false, "use-exchange-order-limits": false, diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 5b60acfdc10..2786e2572d0 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -19,7 +19,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/log" ) // Reset returns the exchange to initial settings @@ -53,8 +52,10 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.ExchangeFee = cs.ExchangeFee f.Direction = o.GetDirection() - var adjustedPrice, amount decimal.Decimal - + var price, adjustedPrice, + amount, adjustedAmount decimal.Decimal + amount = o.GetAmount() + price = o.GetClosePrice() if cs.UseRealOrders { if o.IsLiquidating() { // Liquidation occurs serverside @@ -84,8 +85,8 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * return f, err } // calculate an estimated slippage rate - adjustedPrice, amount = slippage.CalculateSlippageByOrderbook(ob, o.GetDirection(), eventFunds, f.ExchangeFee) - f.Slippage = adjustedPrice.Sub(f.ClosePrice).Div(f.ClosePrice).Mul(decimal.NewFromInt(100)) + price, amount = slippage.CalculateSlippageByOrderbook(ob, o.GetDirection(), eventFunds, f.ExchangeFee) + f.Slippage = price.Sub(f.ClosePrice).Div(f.ClosePrice).Mul(decimal.NewFromInt(100)) } else { slippageRate := slippage.EstimateSlippagePercentage(cs.MinimumSlippageRate, cs.MaximumSlippageRate) if cs.SkipCandleVolumeFitting || o.GetAssetType().IsFutures() { @@ -100,9 +101,15 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * volStr := data.StreamVol() volume := volStr[len(volStr)-1] - f.VolumeAdjustedPrice, amount = ensureOrderFitsWithinHLV(f.ClosePrice, f.Amount, high, low, volume) - if !amount.Equal(f.GetAmount()) { - f.AppendReasonf("Order size shrunk from %v to %v to fit candle", f.Amount, amount) + adjustedPrice, adjustedAmount = ensureOrderFitsWithinHLV(price, amount, high, low, volume) + if !amount.Equal(adjustedAmount) { + f.AppendReasonf("Order size shrunk from %v to %v to fit candle", amount, adjustedAmount) + amount = adjustedAmount + } + if !adjustedPrice.Equal(price) { + f.AppendReasonf("Price adjusted fitting to candle from %v to %v", price, adjustedPrice) + price = adjustedPrice + f.VolumeAdjustedPrice = price } } if amount.LessThanOrEqual(decimal.Zero) && f.GetAmount().GreaterThan(decimal.Zero) { @@ -121,40 +128,39 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.AppendReasonf("amount set to 0, %s", errDataMayBeIncorrect) return f, err } - adjustedPrice = applySlippageToPrice(f.GetDirection(), f.GetVolumeAdjustedPrice(), slippageRate) + adjustedPrice = applySlippageToPrice(f.GetDirection(), price, slippageRate) + if !adjustedPrice.Equal(price) { + f.AppendReasonf("Price has slipped from %v to %v", price, adjustedPrice) + price = adjustedPrice + } f.Slippage = slippageRate.Mul(decimal.NewFromInt(100)).Sub(decimal.NewFromInt(100)) - f.ExchangeFee = calculateExchangeFee(adjustedPrice, amount, cs.TakerFee) + f.ExchangeFee = calculateExchangeFee(price, amount, cs.TakerFee) } - log.Debugf(log.ExchangeSys, "amount %v", amount) - portfolioLimitedAmount := reduceAmountToFitPortfolioLimit(adjustedPrice, amount, eventFunds, f.GetDirection()) - if !portfolioLimitedAmount.Equal(amount) { - f.AppendReasonf("Order size shrunk from %v to %v to remain within portfolio limits", amount, portfolioLimitedAmount) + adjustedAmount = reduceAmountToFitPortfolioLimit(adjustedPrice, amount, eventFunds, f.GetDirection()) + if !adjustedAmount.Equal(amount) { + f.AppendReasonf("Order size shrunk from %v to %v to remain within portfolio limits", amount, adjustedAmount) + amount = adjustedAmount } - log.Debugf(log.ExchangeSys, "portfolioLimitedAmount %v", portfolioLimitedAmount) - limitReducedAmount := portfolioLimitedAmount - log.Debugf(log.ExchangeSys, "limitReducedAmount %v", limitReducedAmount) if cs.CanUseExchangeLimits { // Conforms the amount to the exchange order defined step amount // reducing it when needed - limitReducedAmount = cs.Limits.ConformToDecimalAmount(portfolioLimitedAmount) - log.Debugf(log.ExchangeSys, "limitReducedAmount %v", limitReducedAmount) - if !limitReducedAmount.Equal(portfolioLimitedAmount) { + adjustedAmount = cs.Limits.ConformToDecimalAmount(amount) + if !adjustedAmount.Equal(amount) { f.AppendReasonf("Order size shrunk from %v to %v to remain within exchange step amount limits", - portfolioLimitedAmount, - limitReducedAmount) + adjustedAmount, + amount) + amount = adjustedAmount } } - err = verifyOrderWithinLimits(f, limitReducedAmount, &cs) + err = verifyOrderWithinLimits(f, amount, &cs) if err != nil { return f, err } - log.Debugf(log.ExchangeSys, "limitReducedAmount %v", limitReducedAmount) - f.ExchangeFee = calculateExchangeFee(adjustedPrice, limitReducedAmount, cs.ExchangeFee) - log.Debugf(log.ExchangeSys, "price %v amount %v, fee %v", adjustedPrice, limitReducedAmount, f.GetExchangeFee()) + f.ExchangeFee = calculateExchangeFee(price, amount, cs.ExchangeFee) - orderID, err := e.placeOrder(context.TODO(), adjustedPrice, limitReducedAmount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) + orderID, err := e.placeOrder(context.TODO(), price, amount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) if err != nil { return f, err } @@ -176,7 +182,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.Total = f.PurchasePrice.Mul(f.Amount).Add(f.ExchangeFee) } if !o.IsLiquidating() { - err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, limitReducedAmount, adjustedPrice) + err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, amount, adjustedPrice) if err != nil { return f, err } @@ -288,7 +294,7 @@ func summarisePosition(direction gctorder.Side, orderAmount, orderTotal, orderFe } // verifyOrderWithinLimits conforms the amount to fall into the minimum size and maximum size limit after reduced -func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, cs *Settings) error { +func verifyOrderWithinLimits(f fill.Event, amount decimal.Decimal, cs *Settings) error { if f == nil { return common.ErrNilEvent } @@ -320,13 +326,13 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c } var minOrMax, belowExceed string var size decimal.Decimal - if limitReducedAmount.LessThan(minMax.MinimumSize) && minMax.MinimumSize.GreaterThan(decimal.Zero) { + if amount.LessThan(minMax.MinimumSize) && minMax.MinimumSize.GreaterThan(decimal.Zero) { isBeyondLimit = true belowExceed = "below" minOrMax = "minimum" size = minMax.MinimumSize } - if limitReducedAmount.GreaterThan(minMax.MaximumSize) && minMax.MaximumSize.GreaterThan(decimal.Zero) { + if amount.GreaterThan(minMax.MaximumSize) && minMax.MaximumSize.GreaterThan(decimal.Zero) { isBeyondLimit = true belowExceed = "exceeded" minOrMax = "maximum" @@ -334,7 +340,7 @@ func verifyOrderWithinLimits(f fill.Event, limitReducedAmount decimal.Decimal, c } if isBeyondLimit { f.SetDirection(direction) - e := fmt.Sprintf("Order size %v %s %s size %v", limitReducedAmount, belowExceed, minOrMax, size) + e := fmt.Sprintf("Order size %v %s %s size %v", amount, belowExceed, minOrMax, size) f.AppendReason(e) return errExceededPortfolioLimit } @@ -411,12 +417,17 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal } func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.Decimal) decimal.Decimal { - adjustedPrice := price - if direction == gctorder.Buy { + var adjustedPrice decimal.Decimal + switch direction { + case gctorder.Buy, gctorder.Bid, gctorder.Long: adjustedPrice = price.Add(price.Mul(decimal.NewFromInt(1).Sub(slippageRate))) - } else if direction == gctorder.Sell { + case gctorder.Sell, gctorder.Ask, gctorder.Short: adjustedPrice = price.Mul(slippageRate) } + if adjustedPrice.IsZero() { + adjustedPrice = price + } + return adjustedPrice } @@ -453,27 +464,27 @@ func (e *Exchange) GetCurrencySettings(exch string, a asset.Item, cp currency.Pa return Settings{}, fmt.Errorf("%w for %v %v %v", errNoCurrencySettingsFound, exch, a, cp) } -func ensureOrderFitsWithinHLV(slippagePrice, amount, high, low, volume decimal.Decimal) (adjustedPrice, adjustedAmount decimal.Decimal) { - adjustedPrice = slippagePrice +func ensureOrderFitsWithinHLV(price, amount, high, low, volume decimal.Decimal) (adjustedPrice, adjustedAmount decimal.Decimal) { + adjustedPrice = price if adjustedPrice.LessThan(low) { adjustedPrice = low } if adjustedPrice.GreaterThan(high) { adjustedPrice = high } - if volume.LessThanOrEqual(decimal.Zero) { - return adjustedPrice, adjustedAmount + orderVolume := amount.Mul(adjustedPrice) + if volume.LessThanOrEqual(decimal.Zero) || orderVolume.LessThanOrEqual(volume) { + return adjustedPrice, amount } - currentVolume := amount.Mul(adjustedPrice) - if currentVolume.GreaterThan(volume) { + if orderVolume.GreaterThan(volume) { // reduce the volume to not exceed the total volume of the candle // it is slightly less than the total to still allow for the illusion // that open high low close values are valid with the remaining volume // this is very opinionated - currentVolume = volume.Mul(decimal.NewFromFloat(0.99999999)) + orderVolume = volume.Mul(decimal.NewFromFloat(0.99999999)) } // extract the amount from the adjusted volume - adjustedAmount = currentVolume.Div(adjustedPrice) + adjustedAmount = orderVolume.Div(adjustedPrice) return adjustedPrice, adjustedAmount } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index fd26592331e..7d1d3ae2ee9 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -254,10 +254,10 @@ func TestExecuteOrder(t *testing.T) { AssetType: a, } o := &order.Order{ - Base: ev, - Direction: gctorder.Buy, - Amount: decimal.NewFromInt(10), - AllocatedSize: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Buy, + Amount: decimal.NewFromInt(10), + AllocatedFunds: decimal.NewFromInt(1337), } item := gctkline.Item{ @@ -372,10 +372,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { AssetType: a, } o := &order.Order{ - Base: ev, - Direction: gctorder.Buy, - Amount: decimal.NewFromInt(10), - AllocatedSize: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Buy, + Amount: decimal.NewFromInt(10), + AllocatedFunds: decimal.NewFromInt(1337), } d := &kline.DataFromKline{ @@ -404,10 +404,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { t.Errorf("received %v expected %v", err, errExceededPortfolioLimit) } o = &order.Order{ - Base: ev, - Direction: gctorder.Buy, - Amount: decimal.NewFromInt(10), - AllocatedSize: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Buy, + Amount: decimal.NewFromInt(10), + AllocatedFunds: decimal.NewFromInt(1337), } cs.BuySide.MaximumSize = decimal.Zero cs.BuySide.MinimumSize = decimal.NewFromFloat(0.01) @@ -420,10 +420,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { t.Error("limitReducedAmount adjusted to 0.99999999, direction BUY, should fall in buyside {MinimumSize:0.01 MaximumSize:0 MaximumTotal:0}") } o = &order.Order{ - Base: ev, - Direction: gctorder.Sell, - Amount: decimal.NewFromInt(10), - AllocatedSize: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Sell, + Amount: decimal.NewFromInt(10), + AllocatedFunds: decimal.NewFromInt(1337), } cs.SellSide.MaximumSize = decimal.Zero cs.SellSide.MinimumSize = decimal.NewFromFloat(0.01) @@ -437,10 +437,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { } o = &order.Order{ - Base: ev, - Direction: gctorder.Sell, - Amount: decimal.NewFromFloat(0.5), - AllocatedSize: decimal.NewFromInt(1337), + Base: ev, + Direction: gctorder.Sell, + Amount: decimal.NewFromFloat(0.5), + AllocatedFunds: decimal.NewFromInt(1337), } cs.SellSide.MaximumSize = decimal.Zero cs.SellSide.MinimumSize = decimal.NewFromInt(1) @@ -451,10 +451,10 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { } o = &order.Order{ - Base: ev, - Direction: gctorder.Sell, - Amount: decimal.NewFromFloat(0.02), - AllocatedSize: decimal.NewFromFloat(0.01337), + Base: ev, + Direction: gctorder.Sell, + Amount: decimal.NewFromFloat(0.02), + AllocatedFunds: decimal.NewFromFloat(0.01337), } cs.SellSide.MaximumSize = decimal.Zero cs.SellSide.MinimumSize = decimal.NewFromFloat(0.01) diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index e2701373e7e..e74f7c78f03 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -204,29 +204,23 @@ func (p *Portfolio) sizeOrder(d common.Directioner, cs *exchange.Settings, origi originalOrderSignal.AppendReason("sized order to 0") } switch d.GetDirection() { - case gctorder.Buy, gctorder.Bid: - sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) - case gctorder.Sell, gctorder.Ask: - sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) - case gctorder.Short, gctorder.Long: - sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) + case gctorder.Buy, + gctorder.Bid, + gctorder.Sell, + gctorder.Ask, + gctorder.Short, + gctorder.Long: + sizedOrder.AllocatedFunds = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) case gctorder.ClosePosition: - if originalOrderSignal.AssetType.IsFutures() { - sizedOrder.AllocatedSize = sizedOrder.Amount.Add(estFee) - } else { - err = funds.Reserve(sizedOrder.Amount.Add(estFee), d.GetDirection()) - sizedOrder.AllocatedSize = sizedOrder.Amount - } - //sizedOrder.AllocatedSize = sizedOrder.Amount.Mul(sizedOrder.ClosePrice).Add(estFee) + sizedOrder.AllocatedFunds = sizedOrder.Amount default: return nil, errInvalidDirection } - err = funds.Reserve(sizedOrder.AllocatedSize, d.GetDirection()) + err = funds.Reserve(sizedOrder.AllocatedFunds, d.GetDirection()) if err != nil { sizedOrder.Direction = gctorder.DoNothing return sizedOrder, err } - log.Debugf(log.ExchangeSys, "price %v allocatedsize %v, fee %v", sizedOrder.ClosePrice, sizedOrder.AllocatedSize, estFee) return sizedOrder, nil } @@ -661,7 +655,7 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle Status: gctorder.Liquidated, ClosePrice: ev.GetClosePrice(), Amount: pos.Exposure, - AllocatedSize: pos.Exposure, + AllocatedFunds: pos.Exposure, OrderType: gctorder.Market, LiquidatingPosition: true, }) @@ -690,7 +684,7 @@ func (p *Portfolio) CreateLiquidationOrdersForExchange(ev common.DataEventHandle Status: gctorder.Liquidated, Amount: allFunds[i].Available, OrderType: gctorder.Market, - AllocatedSize: allFunds[i].Available, + AllocatedFunds: allFunds[i].Available, LiquidatingPosition: true, }) } diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index d87128fd445..73c4825fe89 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -9,7 +9,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/log" ) // SizeOrder is responsible for ensuring that the order size is within config limits @@ -65,18 +64,10 @@ func (s *Size) SizeOrder(o order.Event, amountAvailable decimal.Decimal, cs *exc } func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable decimal.Decimal, cs *exchange.Settings, o order.Event) (amount, fee decimal.Decimal, err error) { - if o.GetAmount().IsPositive() { - // when an order amount is already set - // use the pre-set amount and calculate the fee - fee = o.GetAmount().Mul(price).Mul(cs.TakerFee) - log.Debugf(log.ExchangeSys, "orderAmount %v fee %v feeRate %v price %v fullPrice %v", o.GetAmount(), fee, cs.ExchangeFee, price, price.Mul(o.GetAmount())) - return o.GetAmount(), fee, nil - } var portfolioAmount, portfolioFee decimal.Decimal switch direction { case gctorder.ClosePosition: - oneMFeeRate := decimal.NewFromInt(1).Sub(cs.ExchangeFee) - amount = amountAvailable.Mul(oneMFeeRate) + amount = amountAvailable fee = amount.Mul(price).Mul(cs.ExchangeFee) case gctorder.Buy, gctorder.Long: // check size against currency specific settings @@ -96,13 +87,10 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d } case gctorder.Sell, gctorder.Short: // check size against currency specific settings - log.Debugf(log.ExchangeSys, "price %v amount %v, fee %v", price, amount, cs.ExchangeFee) - log.Debugf(log.ExchangeSys, "price %v amount %v, fee %v", price, amountAvailable, cs.ExchangeFee) amount, fee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) if err != nil { return decimal.Decimal{}, decimal.Decimal{}, err } - log.Debugf(log.ExchangeSys, "amount %v, fee %v", amount, fee) // check size against portfolio specific settings portfolioAmount, portfolioFee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) if err != nil { @@ -121,6 +109,13 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d return decimal.Decimal{}, decimal.Decimal{}, fmt.Errorf("%w at %v for %v %v %v, no amount sized", errCannotAllocate, o.GetTime(), o.GetExchange(), o.GetAssetType(), o.Pair()) } + if o.GetAmount().IsPositive() && o.GetAmount().LessThanOrEqual(amount) { + // when an order amount is already set + // use the pre-set amount and calculate the fee + amount = o.GetAmount() + fee = o.GetAmount().Mul(price).Mul(cs.TakerFee) + } + return amount, fee, nil } diff --git a/backtester/eventhandlers/portfolio/size/size_test.go b/backtester/eventhandlers/portfolio/size/size_test.go index aac35d91a0c..c41f616f0ad 100644 --- a/backtester/eventhandlers/portfolio/size/size_test.go +++ b/backtester/eventhandlers/portfolio/size/size_test.go @@ -263,6 +263,7 @@ func TestSizeOrder(t *testing.T) { } o.ClosePrice = decimal.NewFromInt(1000000000) + o.Amount = decimal.NewFromInt(1000000000) _, _, err = s.SizeOrder(o, decimal.NewFromInt(1337), cs) if !errors.Is(err, errCannotAllocate) { t.Errorf("received: %v, expected: %v", err, errCannotAllocate) diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go index cc658a4f921..cd1ed9edb63 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry.go @@ -113,7 +113,9 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS } var response []signal.Event switch { - case len(pos) == 0: + case len(pos) == 0, + pos[len(pos)-1].Status == order.Closed && + diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): // check to see if order is appropriate to action spotSignal.SetPrice(spotSignal.ClosePrice) spotSignal.AppendReasonf("Signalling purchase of %v", spotSignal.Pair()) @@ -135,6 +137,7 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS response = append(response, spotSignal) case pos[len(pos)-1].Status == order.Open && isLastEvent: + // closing positions on last event spotSignal.SetDirection(order.ClosePosition) spotSignal.AppendReason("Selling asset on last event") futuresSignal.SetDirection(order.ClosePosition) @@ -142,15 +145,12 @@ func (s *Strategy) createSignals(pos []order.PositionStats, spotSignal, futuresS response = append(response, futuresSignal, spotSignal) case pos[len(pos)-1].Status == order.Open && diffBetweenFuturesSpot.LessThanOrEqual(s.closeShortDistancePercentage): + // closing positions when custom threshold met + spotSignal.SetDirection(order.ClosePosition) + spotSignal.AppendReasonf("Closing position. Met threshold of %v", s.closeShortDistancePercentage) futuresSignal.SetDirection(order.ClosePosition) - futuresSignal.AppendReasonf("Closing position. Threshold %v", s.closeShortDistancePercentage) - response = append(response, spotSignal, futuresSignal) - case pos[len(pos)-1].Status == order.Closed && - diffBetweenFuturesSpot.GreaterThan(s.openShortDistancePercentage): - futuresSignal.SetDirection(order.Short) - futuresSignal.SetPrice(futuresSignal.ClosePrice) - futuresSignal.AppendReasonf("Opening position. Threshold %v", s.openShortDistancePercentage) - response = append(response, spotSignal, futuresSignal) + futuresSignal.AppendReasonf("Closing position. Met threshold %v", s.closeShortDistancePercentage) + response = append(response, futuresSignal, spotSignal) default: response = append(response, spotSignal, futuresSignal) } diff --git a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go index 7b5a5722e9d..16df50182ec 100644 --- a/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go +++ b/backtester/eventhandlers/strategies/ftxcashandcarry/ftxcashandcarry_test.go @@ -256,14 +256,17 @@ func TestCreateSignals(t *testing.T) { if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v", err, expectedError) } - if len(resp) != 2 { - t.Errorf("received '%v' expected '%v", len(resp), 2) + if len(resp) != 1 { + t.Errorf("received '%v' expected '%v", len(resp), 1) } caseTested = false for i := range resp { - if resp[i].GetAssetType().IsFutures() { - if resp[i].GetDirection() != gctorder.Short { - t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), gctorder.Short) + if resp[i].GetAssetType() == asset.Spot { + if resp[i].GetDirection() != gctorder.Buy { + t.Errorf("received '%v' expected '%v", resp[i].GetDirection(), gctorder.Buy) + } + if resp[i].GetFillDependentEvent() == nil { + t.Errorf("received '%v' expected '%v'", nil, "fill dependent event") } caseTested = true } diff --git a/backtester/eventtypes/order/order.go b/backtester/eventtypes/order/order.go index f0eac6791f9..8dd7edfa3dd 100644 --- a/backtester/eventtypes/order/order.go +++ b/backtester/eventtypes/order/order.go @@ -80,7 +80,7 @@ func (o *Order) SetLeverage(l decimal.Decimal) { // GetAllocatedFunds returns the amount of funds the portfolio manager // has allocated to this potential position func (o *Order) GetAllocatedFunds() decimal.Decimal { - return o.AllocatedSize + return o.AllocatedFunds } // GetFillDependentEvent returns the fill dependent event diff --git a/backtester/eventtypes/order/order_test.go b/backtester/eventtypes/order/order_test.go index bda582765b1..449804aa9ee 100644 --- a/backtester/eventtypes/order/order_test.go +++ b/backtester/eventtypes/order/order_test.go @@ -78,7 +78,7 @@ func TestLeverage(t *testing.T) { func TestGetFunds(t *testing.T) { t.Parallel() o := Order{ - AllocatedSize: decimal.NewFromInt(1337), + AllocatedFunds: decimal.NewFromInt(1337), } funds := o.GetAllocatedFunds() if !funds.Equal(decimal.NewFromInt(1337)) { diff --git a/backtester/eventtypes/order/order_types.go b/backtester/eventtypes/order/order_types.go index e520f0b6752..c51729c7217 100644 --- a/backtester/eventtypes/order/order_types.go +++ b/backtester/eventtypes/order/order_types.go @@ -18,7 +18,7 @@ type Order struct { Amount decimal.Decimal OrderType order.Type Leverage decimal.Decimal - AllocatedSize decimal.Decimal + AllocatedFunds decimal.Decimal BuyLimit decimal.Decimal SellLimit decimal.Decimal FillDependentEvent signal.Event diff --git a/backtester/funding/collateralpair.go b/backtester/funding/collateralpair.go index 598ff257ecb..fd69329fafc 100644 --- a/backtester/funding/collateralpair.go +++ b/backtester/funding/collateralpair.go @@ -7,7 +7,6 @@ import ( "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/currency" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/log" ) // collateral related errors @@ -83,12 +82,9 @@ func (c *CollateralPair) ReleaseContracts(amount decimal.Decimal) error { // Reserve reserves or releases collateral based on order side func (c *CollateralPair) Reserve(amount decimal.Decimal, side gctorder.Side) error { - log.Debugf(log.ExchangeSys, "reserving amount %v", amount) switch side { - case gctorder.Long, gctorder.Short: + case gctorder.Long, gctorder.Short, gctorder.ClosePosition: return c.collateral.Reserve(amount) - case gctorder.ClosePosition: - return c.collateral.Release(amount, amount) default: return fmt.Errorf("%w for %v %v %v. Unknown side %v", errCannotAllocate, diff --git a/backtester/funding/collateralpair_test.go b/backtester/funding/collateralpair_test.go index a61b70b9c2a..6273c85a626 100644 --- a/backtester/funding/collateralpair_test.go +++ b/backtester/funding/collateralpair_test.go @@ -247,7 +247,7 @@ func TestCollateralReserve(t *testing.T) { t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1)) } if !c.collateral.available.Equal(decimal.NewFromInt(1336)) { - t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1336)) + t.Errorf("recevied '%v' expected '%v'", c.collateral.available, decimal.NewFromInt(1336)) } err = c.Reserve(decimal.NewFromInt(1), gctorder.Short) @@ -258,18 +258,18 @@ func TestCollateralReserve(t *testing.T) { t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(2)) } if !c.collateral.available.Equal(decimal.NewFromInt(1335)) { - t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1335)) + t.Errorf("recevied '%v' expected '%v'", c.collateral.available, decimal.NewFromInt(1335)) } err = c.Reserve(decimal.NewFromInt(2), gctorder.ClosePosition) if !errors.Is(err, expectedError) { t.Errorf("recevied '%v' expected '%v'", err, expectedError) } - if !c.collateral.reserved.Equal(decimal.Zero) { + if !c.collateral.reserved.Equal(decimal.NewFromInt(4)) { t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.Zero) } - if !c.collateral.available.Equal(decimal.NewFromInt(1337)) { - t.Errorf("recevied '%v' expected '%v'", c.collateral.reserved, decimal.NewFromInt(1337)) + if !c.collateral.available.Equal(decimal.NewFromInt(1333)) { + t.Errorf("recevied '%v' expected '%v'", c.collateral.available, decimal.NewFromInt(1333)) } expectedError = errCannotAllocate diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index 5edc0cdc326..e0a240ddf7c 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -371,10 +371,10 @@ func (f *FundManager) GenerateReport() *Report { } if len(report.USDTotalsOverTime) > 0 { - report.FinalFunds = report.USDTotalsOverTime[len(report.USDTotalsOverTime)-1].USDValue sort.Slice(report.USDTotalsOverTime, func(i, j int) bool { return report.USDTotalsOverTime[i].Time.Before(report.USDTotalsOverTime[j].Time) }) + report.FinalFunds = report.USDTotalsOverTime[len(report.USDTotalsOverTime)-1].USDValue } report.Items = items diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 5dab4a69a1d..49881e8dcea 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1760,8 +1760,8 @@ {{if ne $ev.PNL nil }} {{ $.Prettify.Decimal8 $ev.PNL.GetExposure}} {{$pair.Base}}-{{$pair.Quote}} {{$ev.PNL.GetDirection}} - {{$ev.PNL.GetUnrealisedPNL.PNL}} {{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}} - {{$ev.PNL.GetRealisedPNL.PNL}} {{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}} + {{$.Prettify.Decimal8 $ev.PNL.GetUnrealisedPNL.PNL}} {{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}} + {{$.Prettify.Decimal8 $ev.PNL.GetRealisedPNL.PNL}} {{if ne $ev.PNL nil }}{{$ev.PNL.GetCollateralCurrency}}{{end}} {{else}} 0 {{$pair.Base}}-{{$pair.Quote}} N/A From 2fa83a259cf8fd64a68ec85c6c1470c15fe217f4 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 16 May 2022 12:23:51 +1000 Subject: [PATCH 152/171] doc regen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b4b909da75..32453ead57e 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| | [thrasher-](https://github.com/thrasher-) | 666 | -| [shazbert](https://github.com/shazbert) | 248 | +| [shazbert](https://github.com/shazbert) | 249 | | [gloriousCode](https://github.com/gloriousCode) | 195 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | | [dependabot[bot]](https://github.com/apps/dependabot) | 73 | From b8bb2b016df2459276183c4184861ac373d67f6d Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 16 May 2022 14:15:03 +1000 Subject: [PATCH 153/171] Now returns err --- backtester/eventhandlers/exchange/exchange.go | 11 ++++++--- .../eventhandlers/exchange/exchange_test.go | 24 +++++++++++++++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 2786e2572d0..bfc2f3c4906 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -128,7 +128,10 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.AppendReasonf("amount set to 0, %s", errDataMayBeIncorrect) return f, err } - adjustedPrice = applySlippageToPrice(f.GetDirection(), price, slippageRate) + adjustedPrice, err = applySlippageToPrice(f.GetDirection(), price, slippageRate) + if err != nil { + return f, err + } if !adjustedPrice.Equal(price) { f.AppendReasonf("Price has slipped from %v to %v", price, adjustedPrice) price = adjustedPrice @@ -416,19 +419,21 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal return orderID, nil } -func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.Decimal) decimal.Decimal { +func applySlippageToPrice(direction gctorder.Side, price, slippageRate decimal.Decimal) (decimal.Decimal, error) { var adjustedPrice decimal.Decimal switch direction { case gctorder.Buy, gctorder.Bid, gctorder.Long: adjustedPrice = price.Add(price.Mul(decimal.NewFromInt(1).Sub(slippageRate))) case gctorder.Sell, gctorder.Ask, gctorder.Short: adjustedPrice = price.Mul(slippageRate) + default: + return decimal.Decimal{}, fmt.Errorf("%v %w", direction, gctorder.ErrSideIsInvalid) } if adjustedPrice.IsZero() { adjustedPrice = price } - return adjustedPrice + return adjustedPrice, nil } // SetExchangeAssetCurrencySettings sets the settings for an exchange, asset, currency diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 7d1d3ae2ee9..17660c66ad0 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -471,14 +471,34 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) { func TestApplySlippageToPrice(t *testing.T) { t.Parallel() - resp := applySlippageToPrice(gctorder.Buy, decimal.NewFromInt(1), decimal.NewFromFloat(0.9)) + resp, err := applySlippageToPrice(gctorder.Buy, decimal.NewFromInt(1), decimal.NewFromFloat(0.9)) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if !resp.Equal(decimal.NewFromFloat(1.1)) { t.Errorf("received: %v, expected: %v", resp, decimal.NewFromFloat(1.1)) } - resp = applySlippageToPrice(gctorder.Sell, decimal.NewFromInt(1), decimal.NewFromFloat(0.9)) + + resp, err = applySlippageToPrice(gctorder.Sell, decimal.NewFromInt(1), decimal.NewFromFloat(0.9)) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } if !resp.Equal(decimal.NewFromFloat(0.9)) { t.Errorf("received: %v, expected: %v", resp, decimal.NewFromFloat(0.9)) } + + resp, err = applySlippageToPrice(gctorder.Sell, decimal.NewFromInt(1), decimal.Zero) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if !resp.Equal(decimal.NewFromFloat(1)) { + t.Errorf("received: %v, expected: %v", resp, decimal.NewFromFloat(1)) + } + + _, err = applySlippageToPrice(gctorder.UnknownSide, decimal.NewFromInt(1), decimal.NewFromFloat(0.9)) + if !errors.Is(err, gctorder.ErrSideIsInvalid) { + t.Errorf("received '%v' expected '%v'", err, nil) + } } func TestReduceAmountToFitPortfolioLimit(t *testing.T) { From 456b90e6f83795ac7a8e695adb80cf5b4cb8ed5f Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Fri, 20 May 2022 14:52:20 +1000 Subject: [PATCH 154/171] Fixes sizing bug issue introduced in PR --- backtester/common/common.go | 7 ++ backtester/common/common_test.go | 91 +++++++++++++++++++ backtester/engine/backtest.go | 2 +- backtester/eventhandlers/exchange/exchange.go | 8 +- .../eventhandlers/portfolio/portfolio.go | 4 +- 5 files changed, 106 insertions(+), 6 deletions(-) diff --git a/backtester/common/common.go b/backtester/common/common.go index 88e34ded67d..952bfeb60b6 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -4,9 +4,16 @@ import ( "fmt" "strings" + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" ) +// CanTransact checks whether an order side is valid +// to the backtester's standards +func CanTransact(side gctorder.Side) bool { + return side.IsLong() || side.IsShort() || side == gctorder.ClosePosition +} + // DataTypeToInt converts the config string value into an int func DataTypeToInt(dataType string) (int64, error) { switch dataType { diff --git a/backtester/common/common_test.go b/backtester/common/common_test.go index 0724cdc1ec1..95b5443d10d 100644 --- a/backtester/common/common_test.go +++ b/backtester/common/common_test.go @@ -3,8 +3,99 @@ package common import ( "fmt" "testing" + + gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) +func TestCanTransact(t *testing.T) { + t.Parallel() + for _, ti := range []struct { + side gctorder.Side + expected bool + }{ + { + side: gctorder.UnknownSide, + expected: false, + }, + { + side: gctorder.Buy, + expected: true, + }, + { + side: gctorder.Sell, + expected: true, + }, + { + side: gctorder.Bid, + expected: true, + }, + { + side: gctorder.Ask, + expected: true, + }, + { + // while anyside can work in GCT, it's a no for the backtester + side: gctorder.AnySide, + expected: false, + }, + { + side: gctorder.Long, + expected: true, + }, + { + side: gctorder.Short, + expected: true, + }, + { + side: gctorder.ClosePosition, + expected: true, + }, + { + side: gctorder.DoNothing, + expected: false, + }, + { + side: gctorder.TransferredFunds, + expected: false, + }, + { + side: gctorder.CouldNotBuy, + expected: false, + }, + { + side: gctorder.CouldNotSell, + expected: false, + }, + { + side: gctorder.CouldNotShort, + expected: false, + }, + { + side: gctorder.CouldNotLong, + expected: false, + }, + { + side: gctorder.CouldNotCloseShort, + expected: false, + }, + { + side: gctorder.CouldNotCloseLong, + expected: false, + }, + { + side: gctorder.MissingData, + expected: false, + }, + } { + t.Run(ti.side.String(), func(t *testing.T) { + t.Parallel() + if CanTransact(ti.side) != ti.expected { + t.Errorf("received '%v' expected '%v'", ti.side, ti.expected) + } + }) + } +} + func TestDataTypeConversion(t *testing.T) { t.Parallel() for _, ti := range []struct { diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index 237e3abb7ea..92b910d1560 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -359,7 +359,7 @@ func (bt *BackTest) processOrderEvent(ev order.Event, funds funding.IFundRelease log.Errorf(common.Backtester, "ExecuteOrder fill event should always be returned, please fix, %v", err) return fmt.Errorf("ExecuteOrder fill event should always be returned, please fix, %v", err) } - if !errors.Is(err, exchange.ErrDoNothing) { + if !errors.Is(err, exchange.ErrCannotTransact) { log.Errorf(common.Backtester, "ExecuteOrder %v %v %v %v", f.GetExchange(), f.GetAssetType(), f.Pair(), err) } } diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index bfc2f3c4906..6d6a8b270cf 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -26,8 +26,8 @@ func (e *Exchange) Reset() { *e = Exchange{} } -// ErrDoNothing returns when its an issue to do nothing for an event -var ErrDoNothing = errors.New("received Do Nothing direction") +// ErrCannotTransact returns when its an issue to do nothing for an event +var ErrCannotTransact = errors.New("cannot transact") // ExecuteOrder assesses the portfolio manager's order event and if it passes validation // will send an order to the exchange/fake order manager to be stored and raise a fill event @@ -40,8 +40,8 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * FillDependentEvent: o.GetFillDependentEvent(), Liquidated: o.IsLiquidating(), } - if o.GetDirection() == gctorder.DoNothing { - return f, ErrDoNothing + if !common.CanTransact(o.GetDirection()) { + return f, fmt.Errorf("%w order direction %v", ErrCannotTransact, o.GetDirection()) } eventFunds := o.GetAllocatedFunds() diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index e74f7c78f03..36285f146c2 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -120,7 +120,9 @@ func (p *Portfolio) OnSignal(ev signal.Event, cs *exchange.Settings, funds fundi if err != nil { return sizedOrder, err } - sizedOrder.SetDirection(side) + if common.CanTransact(sizedOrder.Direction) { + sizedOrder.SetDirection(side) + } if ev.GetDirection() == gctorder.ClosePosition { sizedOrder.ClosingPosition = true } From 7e47e7d15eedb8e8e52433329daedea867742cb5 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 23 May 2022 11:48:01 +1000 Subject: [PATCH 155/171] Fixes fun fee/total US value bug --- backtester/eventhandlers/exchange/exchange.go | 36 +++++++++---------- .../eventhandlers/exchange/exchange_test.go | 30 ++++++++-------- .../exchange/slippage/slippage.go | 4 +-- .../eventhandlers/portfolio/size/size.go | 10 +++--- backtester/funding/item.go | 2 +- backtester/funding/spotpair.go | 2 +- 6 files changed, 41 insertions(+), 43 deletions(-) diff --git a/backtester/eventhandlers/exchange/exchange.go b/backtester/eventhandlers/exchange/exchange.go index 6d6a8b270cf..f69ba3b9cd1 100644 --- a/backtester/eventhandlers/exchange/exchange.go +++ b/backtester/eventhandlers/exchange/exchange.go @@ -44,16 +44,16 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * return f, fmt.Errorf("%w order direction %v", ErrCannotTransact, o.GetDirection()) } - eventFunds := o.GetAllocatedFunds() + allocatedFunds := o.GetAllocatedFunds() cs, err := e.GetCurrencySettings(o.GetExchange(), o.GetAssetType(), o.Pair()) if err != nil { return f, err } - f.ExchangeFee = cs.ExchangeFee f.Direction = o.GetDirection() var price, adjustedPrice, - amount, adjustedAmount decimal.Decimal + amount, adjustedAmount, + fee decimal.Decimal amount = o.GetAmount() price = o.GetClosePrice() if cs.UseRealOrders { @@ -85,7 +85,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * return f, err } // calculate an estimated slippage rate - price, amount = slippage.CalculateSlippageByOrderbook(ob, o.GetDirection(), eventFunds, f.ExchangeFee) + price, amount = slippage.CalculateSlippageByOrderbook(ob, o.GetDirection(), allocatedFunds, f.ExchangeFee) f.Slippage = price.Sub(f.ClosePrice).Div(f.ClosePrice).Mul(decimal.NewFromInt(100)) } else { slippageRate := slippage.EstimateSlippagePercentage(cs.MinimumSlippageRate, cs.MaximumSlippageRate) @@ -137,10 +137,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * price = adjustedPrice } f.Slippage = slippageRate.Mul(decimal.NewFromInt(100)).Sub(decimal.NewFromInt(100)) - f.ExchangeFee = calculateExchangeFee(price, amount, cs.TakerFee) } - adjustedAmount = reduceAmountToFitPortfolioLimit(adjustedPrice, amount, eventFunds, f.GetDirection()) + adjustedAmount = reduceAmountToFitPortfolioLimit(adjustedPrice, amount, allocatedFunds, f.GetDirection()) if !adjustedAmount.Equal(amount) { f.AppendReasonf("Order size shrunk from %v to %v to remain within portfolio limits", amount, adjustedAmount) amount = adjustedAmount @@ -161,9 +160,9 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * if err != nil { return f, err } - f.ExchangeFee = calculateExchangeFee(price, amount, cs.ExchangeFee) - orderID, err := e.placeOrder(context.TODO(), price, amount, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) + fee = calculateExchangeFee(price, amount, cs.TakerFee) + orderID, err := e.placeOrder(context.TODO(), price, amount, fee, cs.UseRealOrders, cs.CanUseExchangeLimits, f, orderManager) if err != nil { return f, err } @@ -185,7 +184,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * f.Total = f.PurchasePrice.Mul(f.Amount).Add(f.ExchangeFee) } if !o.IsLiquidating() { - err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), eventFunds, amount, adjustedPrice) + err = allocateFundsPostOrder(f, funds, err, o.GetAmount(), allocatedFunds, amount, adjustedPrice, fee) if err != nil { return f, err } @@ -198,7 +197,7 @@ func (e *Exchange) ExecuteOrder(o order.Event, data data.Handler, orderManager * return f, nil } -func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderError error, orderAmount, eventFunds, limitReducedAmount, adjustedPrice decimal.Decimal) error { +func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderError error, orderAmount, allocatedFunds, limitReducedAmount, adjustedPrice, fee decimal.Decimal) error { if f == nil { return fmt.Errorf("%w: fill event", common.ErrNilEvent) } @@ -213,7 +212,7 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro return err } if orderError != nil { - err = pr.Release(eventFunds, eventFunds, f.GetDirection()) + err = pr.Release(allocatedFunds, allocatedFunds, f.GetDirection()) if err != nil { f.AppendReason(err.Error()) } @@ -228,7 +227,7 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro switch f.GetDirection() { case gctorder.Buy, gctorder.Bid: - err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount.Mul(adjustedPrice)), f.GetDirection()) + err = pr.Release(allocatedFunds, allocatedFunds.Sub(limitReducedAmount.Mul(adjustedPrice).Add(fee)), f.GetDirection()) if err != nil { return err } @@ -236,8 +235,8 @@ func allocateFundsPostOrder(f *fill.Fill, funds funding.IFundReleaser, orderErro if err != nil { return err } - case gctorder.Sell, gctorder.Ask, gctorder.ClosePosition: - err = pr.Release(eventFunds, eventFunds.Sub(limitReducedAmount), f.GetDirection()) + case gctorder.Sell, gctorder.Ask: + err = pr.Release(allocatedFunds, allocatedFunds.Sub(limitReducedAmount), f.GetDirection()) if err != nil { return err } @@ -366,7 +365,7 @@ func reduceAmountToFitPortfolioLimit(adjustedPrice, amount, sizedPortfolioTotal return amount } -func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal, useRealOrders, useExchangeLimits bool, f fill.Event, orderManager *engine.OrderManager) (string, error) { +func (e *Exchange) placeOrder(ctx context.Context, price, amount, fee decimal.Decimal, useRealOrders, useExchangeLimits bool, f fill.Event, orderManager *engine.OrderManager) (string, error) { if f == nil { return "", common.ErrNilEvent } @@ -376,11 +375,10 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal } var orderID string p := price.InexactFloat64() - fee := f.GetExchangeFee().InexactFloat64() o := &gctorder.Submit{ Price: p, Amount: amount.InexactFloat64(), - Fee: fee, + Fee: fee.InexactFloat64(), Exchange: f.GetExchange(), ID: u.String(), Side: f.GetDirection(), @@ -403,8 +401,8 @@ func (e *Exchange) placeOrder(ctx context.Context, price, amount decimal.Decimal submitResponse := gctorder.SubmitResponse{ IsOrderPlaced: true, OrderID: u.String(), - Rate: f.GetAmount().InexactFloat64(), - Fee: fee, + Rate: o.Amount, + Fee: fee.InexactFloat64(), Cost: p, FullyMatched: true, } diff --git a/backtester/eventhandlers/exchange/exchange_test.go b/backtester/eventhandlers/exchange/exchange_test.go index 17660c66ad0..792408f9100 100644 --- a/backtester/eventhandlers/exchange/exchange_test.go +++ b/backtester/eventhandlers/exchange/exchange_test.go @@ -167,32 +167,32 @@ func TestPlaceOrder(t *testing.T) { t.Error(err) } e := Exchange{} - _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, nil, nil) + _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.Zero, false, true, nil, nil) if !errors.Is(err, common.ErrNilEvent) { t.Errorf("received: %v, expected: %v", err, common.ErrNilEvent) } f := &fill.Fill{ Base: &event.Base{}, } - _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, f, bot.OrderManager) + _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.Zero, false, true, f, bot.OrderManager) if !errors.Is(err, engine.ErrExchangeNameIsEmpty) { t.Errorf("received: %v, expected: %v", err, engine.ErrExchangeNameIsEmpty) } f.Exchange = testExchange - _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, f, bot.OrderManager) + _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.Zero, false, true, f, bot.OrderManager) if !errors.Is(err, gctorder.ErrPairIsEmpty) { t.Errorf("received: %v, expected: %v", err, gctorder.ErrPairIsEmpty) } f.CurrencyPair = currency.NewPair(currency.BTC, currency.USDT) f.AssetType = asset.Spot f.Direction = gctorder.Buy - _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), false, true, f, bot.OrderManager) + _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.Zero, false, true, f, bot.OrderManager) if err != nil { t.Error(err) } - _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), true, true, f, bot.OrderManager) + _, err = e.placeOrder(context.Background(), decimal.NewFromInt(1), decimal.NewFromInt(1), decimal.Zero, true, true, f, bot.OrderManager) if !errors.Is(err, exchange.ErrAuthenticationSupportNotEnabled) { t.Errorf("received: %v but expected: %v", err, exchange.ErrAuthenticationSupportNotEnabled) } @@ -581,7 +581,7 @@ func TestVerifyOrderWithinLimits(t *testing.T) { func TestAllocateFundsPostOrder(t *testing.T) { t.Parallel() expectedError := common.ErrNilEvent - err := allocateFundsPostOrder(nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero) + err := allocateFundsPostOrder(nil, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } @@ -593,7 +593,7 @@ func TestAllocateFundsPostOrder(t *testing.T) { }, Direction: gctorder.Buy, } - err = allocateFundsPostOrder(f, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero) + err = allocateFundsPostOrder(f, nil, nil, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } @@ -621,19 +621,19 @@ func TestAllocateFundsPostOrder(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, expectedError) } f.Order = &gctorder.Detail{} - err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one) + err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } f.SetDirection(gctorder.Sell) - err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one) + err = allocateFundsPostOrder(f, fundPair, nil, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } expectedError = gctorder.ErrSubmissionIsNil orderError := gctorder.ErrSubmissionIsNil - err = allocateFundsPostOrder(f, fundPair, orderError, one, one, one, one) + err = allocateFundsPostOrder(f, fundPair, orderError, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } @@ -663,31 +663,31 @@ func TestAllocateFundsPostOrder(t *testing.T) { } expectedError = gctorder.ErrSubmissionIsNil - err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one) + err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } expectedError = nil - err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one) + err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } expectedError = gctorder.ErrSubmissionIsNil f.SetDirection(gctorder.Long) - err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one) + err = allocateFundsPostOrder(f, collateralPair, orderError, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } expectedError = nil - err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one) + err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } f.AssetType = asset.Margin expectedError = common.ErrInvalidDataType - err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one) + err = allocateFundsPostOrder(f, collateralPair, nil, one, one, one, one, decimal.Zero) if !errors.Is(err, expectedError) { t.Errorf("received '%v' expected '%v'", err, expectedError) } diff --git a/backtester/eventhandlers/exchange/slippage/slippage.go b/backtester/eventhandlers/exchange/slippage/slippage.go index efed0e02d88..364e7edf163 100644 --- a/backtester/eventhandlers/exchange/slippage/slippage.go +++ b/backtester/eventhandlers/exchange/slippage/slippage.go @@ -31,8 +31,8 @@ func EstimateSlippagePercentage(maximumSlippageRate, minimumSlippageRate decimal // CalculateSlippageByOrderbook will analyse a provided orderbook and return the result of attempting to // place the order on there -func CalculateSlippageByOrderbook(ob *orderbook.Base, side gctorder.Side, amountOfFunds, feeRate decimal.Decimal) (price, amount decimal.Decimal) { - result := ob.SimulateOrder(amountOfFunds.InexactFloat64(), side == gctorder.Buy) +func CalculateSlippageByOrderbook(ob *orderbook.Base, side gctorder.Side, allocatedFunds, feeRate decimal.Decimal) (price, amount decimal.Decimal) { + result := ob.SimulateOrder(allocatedFunds.InexactFloat64(), side == gctorder.Buy) rate := (result.MinimumPrice - result.MaximumPrice) / result.MaximumPrice price = decimal.NewFromFloat(result.MinimumPrice * (rate + 1)) amount = decimal.NewFromFloat(result.Amount * (1 - feeRate.InexactFloat64())) diff --git a/backtester/eventhandlers/portfolio/size/size.go b/backtester/eventhandlers/portfolio/size/size.go index 73c4825fe89..092b3be2dbd 100644 --- a/backtester/eventhandlers/portfolio/size/size.go +++ b/backtester/eventhandlers/portfolio/size/size.go @@ -68,15 +68,15 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d switch direction { case gctorder.ClosePosition: amount = amountAvailable - fee = amount.Mul(price).Mul(cs.ExchangeFee) + fee = amount.Mul(price).Mul(cs.TakerFee) case gctorder.Buy, gctorder.Long: // check size against currency specific settings - amount, fee, err = s.calculateBuySize(price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), cs.BuySide) + amount, fee, err = s.calculateBuySize(price, amountAvailable, cs.TakerFee, o.GetBuyLimit(), cs.BuySide) if err != nil { return decimal.Decimal{}, decimal.Decimal{}, err } // check size against portfolio specific settings - portfolioAmount, portfolioFee, err = s.calculateBuySize(price, amountAvailable, cs.ExchangeFee, o.GetBuyLimit(), s.BuySide) + portfolioAmount, portfolioFee, err = s.calculateBuySize(price, amountAvailable, cs.TakerFee, o.GetBuyLimit(), s.BuySide) if err != nil { return decimal.Decimal{}, decimal.Decimal{}, err } @@ -87,12 +87,12 @@ func (s *Size) calculateAmount(direction gctorder.Side, price, amountAvailable d } case gctorder.Sell, gctorder.Short: // check size against currency specific settings - amount, fee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), cs.SellSide) + amount, fee, err = s.calculateSellSize(price, amountAvailable, cs.TakerFee, o.GetSellLimit(), cs.SellSide) if err != nil { return decimal.Decimal{}, decimal.Decimal{}, err } // check size against portfolio specific settings - portfolioAmount, portfolioFee, err = s.calculateSellSize(price, amountAvailable, cs.ExchangeFee, o.GetSellLimit(), s.SellSide) + portfolioAmount, portfolioFee, err = s.calculateSellSize(price, amountAvailable, cs.TakerFee, o.GetSellLimit(), s.SellSide) if err != nil { return decimal.Decimal{}, decimal.Decimal{}, err } diff --git a/backtester/funding/item.go b/backtester/funding/item.go index 713b9b58116..7da21c86474 100644 --- a/backtester/funding/item.go +++ b/backtester/funding/item.go @@ -35,7 +35,7 @@ func (i *Item) Release(amount, diff decimal.Decimal) error { return errZeroAmountReceived } if diff.IsNegative() && !i.asset.IsFutures() { - return fmt.Errorf("%w diff", errNegativeAmountReceived) + return fmt.Errorf("%w diff %v", errNegativeAmountReceived, diff) } if amount.GreaterThan(i.reserved) { return fmt.Errorf("%w for %v %v %v. Requested %v Reserved: %v", diff --git a/backtester/funding/spotpair.go b/backtester/funding/spotpair.go index 559ac4484c1..17595eb0142 100644 --- a/backtester/funding/spotpair.go +++ b/backtester/funding/spotpair.go @@ -63,7 +63,7 @@ func (p *SpotPair) Release(amount, diff decimal.Decimal, side order.Side) error switch side { case order.Buy, order.Bid: return p.quote.Release(amount, diff) - case order.Sell, order.Ask, order.ClosePosition: + case order.Sell, order.Ask: return p.base.Release(amount, diff) } return fmt.Errorf("%w for %v %v %v. Unknown side %v", From 97aa13e4f6b7fe2635552d4148c74d93634d92fa Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Mon, 23 May 2022 12:19:36 +1000 Subject: [PATCH 156/171] Fix chart bug. Show log charts with disclaimer --- backtester/funding/funding.go | 2 +- backtester/report/chart.go | 34 +++++++++++++++++++------------ backtester/report/chart_test.go | 4 ++-- backtester/report/report_types.go | 5 +++-- backtester/report/tpl.gohtml | 6 ++++++ 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/backtester/funding/funding.go b/backtester/funding/funding.go index e0a240ddf7c..65bed25a298 100644 --- a/backtester/funding/funding.go +++ b/backtester/funding/funding.go @@ -328,7 +328,7 @@ func (f *FundManager) GenerateReport() *Report { if f.items[x].asset.IsFutures() || f.disableUSDTracking { // futures contracts / collateral does not contribute to USD value // no USD tracking means no USD values to breakdown - break + continue } for y := range report.USDTotalsOverTime { if report.USDTotalsOverTime[y].Time.Equal(snapshot.Time) { diff --git a/backtester/report/chart.go b/backtester/report/chart.go index 41b2d2a0aa3..49d6d94f2a2 100644 --- a/backtester/report/chart.go +++ b/backtester/report/chart.go @@ -19,7 +19,9 @@ func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.Fun if stats == nil { return nil, fmt.Errorf("%w missing funding item statistics", common.ErrNilArguments) } - response := &Chart{} + response := &Chart{ + AxisType: "logarithmic", + } usdTotalChartPlot := make([]LinePlot, len(items)) for i := range items { usdTotalChartPlot[i] = LinePlot{ @@ -35,6 +37,9 @@ func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.Fun for i := range stats { var plots []LinePlot for j := range stats[i].ReportItem.Snapshots { + if stats[i].ReportItem.Snapshots[j].Available.IsZero() { + response.ShowZeroDisclaimer = true + } plots = append(plots, LinePlot{ Value: stats[i].ReportItem.Snapshots[j].USDValue.InexactFloat64(), UnixMilli: stats[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), @@ -51,27 +56,26 @@ func createUSDTotalsChart(items []statistics.ValueAtTime, stats []statistics.Fun // createHoldingsOverTimeChart used for creating a chart in the HTML report // to show how many holdings of each type was held over the time of backtesting -func createHoldingsOverTimeChart(items []statistics.FundingItemStatistics) (*Chart, error) { - if items == nil { +func createHoldingsOverTimeChart(stats []statistics.FundingItemStatistics) (*Chart, error) { + if stats == nil { return nil, fmt.Errorf("%w missing funding item statistics", common.ErrNilArguments) } response := &Chart{ AxisType: "logarithmic", } - for i := range items { + for i := range stats { var plots []LinePlot - for j := range items[i].ReportItem.Snapshots { - if items[i].ReportItem.Snapshots[j].Available.IsZero() { - // highcharts can't render zeroes in logarithmic mode - response.AxisType = "linear" + for j := range stats[i].ReportItem.Snapshots { + if stats[i].ReportItem.Snapshots[j].Available.IsZero() { + response.ShowZeroDisclaimer = true } plots = append(plots, LinePlot{ - Value: items[i].ReportItem.Snapshots[j].Available.InexactFloat64(), - UnixMilli: items[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), + UnixMilli: stats[i].ReportItem.Snapshots[j].Time.UTC().UnixMilli(), + Value: stats[i].ReportItem.Snapshots[j].Available.InexactFloat64(), }) } response.Data = append(response.Data, ChartLine{ - Name: fmt.Sprintf("%v %v %v holdings", items[i].ReportItem.Exchange, items[i].ReportItem.Asset, items[i].ReportItem.Currency), + Name: fmt.Sprintf("%v %v %v holdings", stats[i].ReportItem.Exchange, stats[i].ReportItem.Asset, stats[i].ReportItem.Currency), LinePlots: plots, }) } @@ -85,7 +89,9 @@ func createPNLCharts(items map[string]map[asset.Item]map[currency.Pair]*statisti if items == nil { return nil, fmt.Errorf("%w missing currency pair statistics", common.ErrNilArguments) } - response := &Chart{} + response := &Chart{ + AxisType: "linear", + } for exch, assetMap := range items { for item, pairMap := range assetMap { for pair, result := range pairMap { @@ -127,7 +133,9 @@ func createFuturesSpotDiffChart(items map[string]map[asset.Item]map[currency.Pai return nil, fmt.Errorf("%w missing currency pair statistics", common.ErrNilArguments) } currs := make(map[currency.Pair]linkCurrencyDiff) - response := &Chart{} + response := &Chart{ + AxisType: "linear", + } for _, assetMap := range items { for item, pairMap := range assetMap { diff --git a/backtester/report/chart_test.go b/backtester/report/chart_test.go index f53066341ab..642961cfa64 100644 --- a/backtester/report/chart_test.go +++ b/backtester/report/chart_test.go @@ -92,8 +92,8 @@ func TestCreateHoldingsOverTimeChart(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, nil) } - if resp.AxisType != "linear" { - t.Error("expected linear from zero available") + if !resp.ShowZeroDisclaimer { + t.Error("expected ShowZeroDisclaimer") } } diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index 82c55094539..867c6901f80 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -48,8 +48,9 @@ type Data struct { // Chart holds chart data along with an axis type Chart struct { - AxisType string - Data []ChartLine + AxisType string + ShowZeroDisclaimer bool + Data []ChartLine } // ChartLine holds chart plot data diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index 49881e8dcea..0e98b5bca49 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -916,6 +916,9 @@ {{ if eq $.Config.StrategySettings.DisableUSDTracking false }}

USD Totals

+ {{ if .USDTotalsChart.ShowZeroDisclaimer}} + Note: zero values are not rendered on chart. If line abruptly ends, it is because its value is zero + {{end}}
- + From 501b8e7a76243ba4e15a38756fe5179cb79fc83a Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 30 Jun 2022 10:25:40 +1000 Subject: [PATCH 168/171] Removes bad stats. --- .../eventhandlers/portfolio/holdings/holdings.go | 8 -------- .../portfolio/holdings/holdings_test.go | 12 ------------ .../portfolio/holdings/holdings_types.go | 4 ---- backtester/eventhandlers/statistics/printresults.go | 7 +++---- .../eventhandlers/statistics/statistics_test.go | 4 ---- backtester/report/tpl.gohtml | 8 -------- 6 files changed, 3 insertions(+), 40 deletions(-) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index eb3dc66b710..95468d78ba7 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -113,12 +113,10 @@ func (h *Holding) update(e fill.Event, f funding.IFundReader) error { case order.Buy, order.Bid: h.BoughtAmount = h.BoughtAmount.Add(amount) - h.ScaledBoughtValue = h.BoughtAmount.Mul(price) h.CommittedFunds = h.BaseSize.Mul(price) case order.Sell, order.Ask: h.SoldAmount = h.SoldAmount.Add(amount) - h.ScaledSoldValue = h.SoldAmount.Mul(price) h.CommittedFunds = h.BaseSize.Mul(price) } @@ -134,18 +132,12 @@ func (h *Holding) update(e fill.Event, f funding.IFundReader) error { func (h *Holding) scaleValuesToCurrentPrice(currentPrice decimal.Decimal) { origPosValue := h.BaseValue - origBoughtValue := h.ScaledBoughtValue - origSoldValue := h.ScaledSoldValue origTotalValue := h.TotalValue h.BaseValue = h.BaseSize.Mul(currentPrice) - h.ScaledBoughtValue = h.BoughtAmount.Mul(currentPrice) - h.ScaledSoldValue = h.SoldAmount.Mul(currentPrice) h.TotalValue = h.BaseValue.Add(h.QuoteSize) h.TotalValueDifference = h.TotalValue.Sub(origTotalValue) - h.BoughtValueDifference = h.ScaledBoughtValue.Sub(origBoughtValue) h.PositionsValueDifference = h.BaseValue.Sub(origPosValue) - h.SoldValueDifference = h.ScaledSoldValue.Sub(origSoldValue) if !origTotalValue.IsZero() { h.ChangeInTotalValuePercent = h.TotalValue.Sub(origTotalValue).Div(origTotalValue) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index d73985f8ff9..998bb2d5426 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -184,9 +184,6 @@ func TestUpdateBuyStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(500)) { - t.Errorf("expected '%v' received '%v'", 500, h.ScaledBoughtValue) - } if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) } @@ -231,9 +228,6 @@ func TestUpdateBuyStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromFloat(1.5)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(750)) { - t.Errorf("expected '%v' received '%v'", 750, h.ScaledBoughtValue) - } if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) } @@ -313,9 +307,6 @@ func TestUpdateSellStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(500)) { - t.Errorf("expected '%v' received '%v'", 500, h.ScaledBoughtValue) - } if !h.SoldAmount.IsZero() { t.Errorf("expected '%v' received '%v'", 0, h.SoldAmount) } @@ -360,9 +351,6 @@ func TestUpdateSellStats(t *testing.T) { if !h.BoughtAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.BoughtAmount) } - if !h.ScaledBoughtValue.Equal(decimal.NewFromInt(500)) { - t.Errorf("expected '%v' received '%v'", 500, h.ScaledBoughtValue) - } if !h.SoldAmount.Equal(decimal.NewFromInt(1)) { t.Errorf("expected '%v' received '%v'", 1, h.SoldAmount) } diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_types.go b/backtester/eventhandlers/portfolio/holdings/holdings_types.go index 8846e8ac915..ad7fa7d71ec 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_types.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_types.go @@ -29,18 +29,14 @@ type Holding struct { TotalInitialValue decimal.Decimal `json:"total-initial-value"` QuoteSize decimal.Decimal `json:"quote-size"` SoldAmount decimal.Decimal `json:"sold-amount"` - ScaledSoldValue decimal.Decimal `json:"scaled-sold-value"` SoldValue decimal.Decimal `json:"sold-value"` BoughtAmount decimal.Decimal `json:"bought-amount"` - ScaledBoughtValue decimal.Decimal `json:"scaled-bought-value"` CommittedFunds decimal.Decimal `json:"committed-funds"` IsLiquidated bool TotalValueDifference decimal.Decimal ChangeInTotalValuePercent decimal.Decimal - BoughtValueDifference decimal.Decimal - SoldValueDifference decimal.Decimal PositionsValueDifference decimal.Decimal TotalValue decimal.Decimal `json:"total-value"` diff --git a/backtester/eventhandlers/statistics/printresults.go b/backtester/eventhandlers/statistics/printresults.go index c21660b6929..208b599c515 100644 --- a/backtester/eventhandlers/statistics/printresults.go +++ b/backtester/eventhandlers/statistics/printresults.go @@ -188,16 +188,15 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency. log.Infof(common.CurrencyStatistics, "%s Lowest Unrealised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestUnrealisedPNL.Value, 8, ".", ","), c.LowestUnrealisedPNL.Time) log.Infof(common.CurrencyStatistics, "%s Highest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestRealisedPNL.Value, 8, ".", ","), c.HighestRealisedPNL.Time) log.Infof(common.CurrencyStatistics, "%s Lowest Realised PNL: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestRealisedPNL.Value, 8, ".", ","), c.LowestRealisedPNL.Time) + log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.UnderlyingPair.Quote, c.HighestCommittedFunds.Time) } else { log.Infof(common.CurrencyStatistics, "%s Buy orders: %s", sep, convert.IntToHumanFriendlyString(c.BuyOrders, ",")) log.Infof(common.CurrencyStatistics, "%s Buy amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.BoughtAmount, 8, ".", ","), last.Holdings.Pair.Base) - log.Infof(common.CurrencyStatistics, "%s Bought amount valued at last candle: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.ScaledBoughtValue, 8, ".", ",")) log.Infof(common.CurrencyStatistics, "%s Sell orders: %s", sep, convert.IntToHumanFriendlyString(c.SellOrders, ",")) - log.Infof(common.CurrencyStatistics, "%s Sell amount: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ",")) - log.Infof(common.CurrencyStatistics, "%s Sold amount valued at last candle: %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.ScaledSoldValue, 8, ".", ",")) + log.Infof(common.CurrencyStatistics, "%s Sell amount: %s %s", sep, convert.DecimalToHumanFriendlyString(last.Holdings.SoldAmount, 8, ".", ","), last.Holdings.Pair.Base) + log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), last.Holdings.Pair.Quote, c.HighestCommittedFunds.Time) } - log.Infof(common.CurrencyStatistics, "%s Highest committed funds: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.HighestCommittedFunds.Value, 8, ".", ","), c.HighestCommittedFunds.Time) log.Infof(common.CurrencyStatistics, "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ",")) log.Info(common.CurrencyStatistics, common.ColourH2+"------------------Max Drawdown-------------------------------"+common.ColourDefault) diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index b5639710103..aadb871fd77 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -284,14 +284,10 @@ func TestAddHoldingsForTime(t *testing.T) { BaseSize: eleet, BaseValue: eleet, SoldAmount: eleet, - ScaledSoldValue: eleet, BoughtAmount: eleet, - ScaledBoughtValue: eleet, QuoteSize: eleet, TotalValueDifference: eleet, ChangeInTotalValuePercent: eleet, - BoughtValueDifference: eleet, - SoldValueDifference: eleet, PositionsValueDifference: eleet, TotalValue: eleet, TotalFees: eleet, diff --git a/backtester/report/tpl.gohtml b/backtester/report/tpl.gohtml index ffa14cfb10b..ee553f17ec4 100644 --- a/backtester/report/tpl.gohtml +++ b/backtester/report/tpl.gohtml @@ -1270,10 +1270,6 @@ Buy Orders {{ $.Prettify.Int $val.BuyOrders}} - - Buy Value - {{ $.Prettify.Decimal8 $val.FinalHoldings.ScaledBoughtValue}} {{$val.FinalHoldings.Pair.Quote}} - Buy Amount {{ $.Prettify.Decimal8 $val.FinalHoldings.BoughtAmount}} {{$val.FinalHoldings.Pair.Base}} @@ -1282,10 +1278,6 @@ Sell Orders {{ $.Prettify.Int $val.SellOrders}} - - Sell Value - {{ $.Prettify.Decimal8 $val.FinalHoldings.ScaledSoldValue}} {{$val.FinalHoldings.Pair.Quote}} - Sell Amount {{ $.Prettify.Decimal8 $val.FinalHoldings.SoldAmount}} {{$val.FinalHoldings.Pair.Base}} From 97c82a95fe9d345665bea2a61cf896b93d7074f4 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 30 Jun 2022 14:22:03 +1000 Subject: [PATCH 169/171] neatens config builder. Moves filename generator --- backtester/common/common.go | 19 ++++ backtester/common/common_test.go | 32 +++++++ backtester/common/common_types.go | 2 + backtester/config/configbuilder/main.go | 111 +++++++++++++++--------- backtester/report/report.go | 29 ++----- backtester/report/report_test.go | 37 -------- backtester/report/report_types.go | 5 +- 7 files changed, 133 insertions(+), 102 deletions(-) diff --git a/backtester/common/common.go b/backtester/common/common.go index 952bfeb60b6..5fde4ffb3f1 100644 --- a/backtester/common/common.go +++ b/backtester/common/common.go @@ -2,6 +2,7 @@ package common import ( "fmt" + "regexp" "strings" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -26,6 +27,24 @@ func DataTypeToInt(dataType string) (int64, error) { } } +// GenerateFileName will convert a proposed filename into something that is more +// OS friendly +func GenerateFileName(fileName, extension string) (string, error) { + if fileName == "" { + return "", fmt.Errorf("%w missing filename", errCannotGenerateFileName) + } + if extension == "" { + return "", fmt.Errorf("%w missing filename extension", errCannotGenerateFileName) + } + + reg := regexp.MustCompile(`[\w-]`) + parsedFileName := reg.FindAllString(fileName, -1) + parsedExtension := reg.FindAllString(extension, -1) + fileName = strings.Join(parsedFileName, "") + "." + strings.Join(parsedExtension, "") + + return strings.ToLower(fileName), nil +} + // FitStringToLimit ensures a string is of the length of the limit // either by truncating the string with ellipses or padding with the spacer func FitStringToLimit(str, spacer string, limit int, upper bool) string { diff --git a/backtester/common/common_test.go b/backtester/common/common_test.go index 95b5443d10d..aee94e9fb9d 100644 --- a/backtester/common/common_test.go +++ b/backtester/common/common_test.go @@ -1,6 +1,7 @@ package common import ( + "errors" "fmt" "testing" @@ -212,3 +213,34 @@ func TestPurgeColours(t *testing.T) { t.Error("expected purged colour") } } + +func TestGenerateFileName(t *testing.T) { + t.Parallel() + _, err := GenerateFileName("", "") + if !errors.Is(err, errCannotGenerateFileName) { + t.Errorf("received '%v' expected '%v'", err, errCannotGenerateFileName) + } + + _, err = GenerateFileName("hello", "") + if !errors.Is(err, errCannotGenerateFileName) { + t.Errorf("received '%v' expected '%v'", err, errCannotGenerateFileName) + } + + _, err = GenerateFileName("", "moto") + if !errors.Is(err, errCannotGenerateFileName) { + t.Errorf("received '%v' expected '%v'", err, errCannotGenerateFileName) + } + + _, err = GenerateFileName("hello", "moto") + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + name, err := GenerateFileName("......HELL0. + _", "moto.") + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if name != "hell0_.moto" { + t.Errorf("received '%v' expected '%v'", name, "hell0_.moto") + } +} diff --git a/backtester/common/common_types.go b/backtester/common/common_types.go index 09afc7658fd..83596106484 100644 --- a/backtester/common/common_types.go +++ b/backtester/common/common_types.go @@ -32,6 +32,8 @@ var ( ErrNilEvent = errors.New("nil event received") // ErrInvalidDataType occurs when an invalid data type is defined in the config ErrInvalidDataType = errors.New("invalid datatype received") + + errCannotGenerateFileName = errors.New("cannot generate filename") ) // EventHandler interface implements required GetTime() & Pair() return diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index c466089569a..cf946ec3bab 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -44,54 +44,58 @@ func main() { fmt.Println("Welcome to the config generator!") reader := bufio.NewReader(os.Stdin) var cfg config.Config - fmt.Println("-----Strategy Settings-----") var err error - firstRun := true - for err != nil || firstRun { - firstRun = false + + fmt.Println("-----Strategy Settings-----") + // loop in sections, so that if there is an error, + // a user only needs to redo that section + for { err = parseStrategySettings(&cfg, reader) if err != nil { log.Println(err) + } else { + break } } fmt.Println("-----Exchange Settings-----") - firstRun = true - for err != nil || firstRun { - firstRun = false + + for { err = parseExchangeSettings(reader, &cfg) if err != nil { log.Println(err) + } else { + break } } fmt.Println("-----Portfolio Settings-----") - firstRun = true - for err != nil || firstRun { - firstRun = false + for { err = parsePortfolioSettings(reader, &cfg) if err != nil { log.Println(err) + } else { + break } } fmt.Println("-----Data Settings-----") - firstRun = true - for err != nil || firstRun { - firstRun = false + for { err = parseDataSettings(&cfg, reader) if err != nil { log.Println(err) + } else { + break } } fmt.Println("-----Statistics Settings-----") - firstRun = true - for err != nil || firstRun { - firstRun = false + for { err = parseStatisticsSettings(&cfg, reader) if err != nil { log.Println(err) + } else { + break } } @@ -103,26 +107,45 @@ func main() { fmt.Println("Write strategy config to file? If no, the output will be on screen y/n") yn := quickParse(reader) if yn == y || yn == yes { - var wd string - wd, err = os.Getwd() - if err != nil { - log.Fatal(err) - } - fn := cfg.StrategySettings.Name - if cfg.Nickname != "" { - fn += "-" + cfg.Nickname - } - fn += ".strat" // nolint:misspell // its shorthand for strategy - wd = filepath.Join(wd, fn) - fmt.Printf("Enter output file. If blank, will output to \"%v\"\n", wd) - path := quickParse(reader) - if path == "" { - path = wd - } - err = os.WriteFile(path, resp, file.DefaultPermissionOctal) - if err != nil { - log.Fatal(err) + var fp, wd string + for { + wd, err = os.Getwd() + if err != nil { + log.Fatal(err) + } + fmt.Printf("Enter output directory. If blank, will default to \"%v\"\n", wd) + parsedPath := quickParse(reader) + if parsedPath != "" { + wd = parsedPath + } + + fn := cfg.StrategySettings.Name + if cfg.Nickname != "" { + fn += "-" + cfg.Nickname + } + fn, err = common.GenerateFileName(fn, "start") + if err != nil { + log.Printf("could not write file, please try again. err: %v", err) + continue + } + fmt.Printf("Enter output file. If blank, will default to \"%v\"\n", fn) + parsedFileName := quickParse(reader) + if parsedFileName != "" { + fn, err = common.GenerateFileName(parsedFileName, "start") + if err != nil { + log.Printf("could not write file, please try again. err: %v", err) + continue + } + } + fp = filepath.Join(wd, fn) + err = os.WriteFile(fp, resp, file.DefaultPermissionOctal) + if err != nil { + log.Printf("could not write file, please try again. err: %v", err) + continue + } + break } + fmt.Printf("Successfully output strategy to \"%v\"\n", fp) } else { log.Print(string(resp)) } @@ -320,7 +343,7 @@ func parseAPI(reader *bufio.Reader, cfg *config.Config) error { cfg.DataSettings.APIData.StartDate = defaultStart } - fmt.Printf("What is the end date? Leave blank for \"%v\"\n", defaultStart.Format(gctcommon.SimpleTimeFormat)) + fmt.Printf("What is the end date? Leave blank for \"%v\"\n", defaultEnd.Format(gctcommon.SimpleTimeFormat)) endDate = quickParse(reader) if endDate != "" { cfg.DataSettings.APIData.EndDate, err = time.Parse(gctcommon.SimpleTimeFormat, endDate) @@ -360,7 +383,7 @@ func parseDatabase(reader *bufio.Reader, cfg *config.Config) error { cfg.DataSettings.DatabaseData.StartDate = defaultStart } - fmt.Printf("What is the end date? Leave blank for \"%v\"\n", defaultStart.Format(gctcommon.SimpleTimeFormat)) + fmt.Printf("What is the end date? Leave blank for \"%v\"\n", defaultEnd.Format(gctcommon.SimpleTimeFormat)) if endDate := quickParse(reader); endDate != "" { cfg.DataSettings.DatabaseData.EndDate, err = time.Parse(gctcommon.SimpleTimeFormat, endDate) if err != nil { @@ -563,7 +586,6 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the currency base. eg BTC") setting.Base = currency.NewCode(quickParse(reader)) if setting.Asset == asset.Spot { - setting.SpotDetails = &config.SpotDetails{} if !usingExchangeLevelFunding { fmt.Println("Enter the initial base funds. eg 0") parseNum := quickParse(reader) @@ -572,14 +594,15 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* if err != nil { return nil, err } - setting.SpotDetails.InitialBaseFunds = &d + setting.SpotDetails = &config.SpotDetails{ + InitialBaseFunds: &d, + } } } } fmt.Println("Enter the currency quote. eg USDT") setting.Quote = currency.NewCode(quickParse(reader)) - if setting.Asset == asset.Spot && !usingExchangeLevelFunding { fmt.Println("Enter the initial quote funds. eg 10000") parseNum := quickParse(reader) @@ -588,7 +611,13 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* if err != nil { return nil, err } - setting.SpotDetails.InitialQuoteFunds = &d + if setting.SpotDetails == nil { + setting.SpotDetails = &config.SpotDetails{ + InitialQuoteFunds: &d, + } + } else { + setting.SpotDetails.InitialQuoteFunds = &d + } } } diff --git a/backtester/report/report.go b/backtester/report/report.go index 01286ddd2de..1505d65156d 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -5,7 +5,6 @@ import ( "html/template" "os" "path/filepath" - "regexp" "strings" "time" @@ -70,7 +69,14 @@ func (d *Data) GenerateReport() error { tmpl := template.Must( template.ParseFiles(d.TemplatePath), ) - fileName, err := generateFileName(d.Config.Nickname, d.Statistics.StrategyName) + fn := d.Config.Nickname + if fn != "" { + fn += "-" + } + fn += d.Statistics.StrategyName + "-" + fn += time.Now().Format("2006-01-02-15-04-05") + + fileName, err := common.GenerateFileName(fn, "html") if err != nil { return err } @@ -98,25 +104,6 @@ func (d *Data) GenerateReport() error { return nil } -func generateFileName(configNickName, strategyName string) (string, error) { - if strategyName == "" { - return "", fmt.Errorf("%w missing strategy name", errCannotGenerateFileName) - } - if configNickName != "" { - configNickName += "-" - } - fileName := fmt.Sprintf( - "%v%v-%v", - configNickName, - strategyName, - time.Now().Format("2006-01-02-15-04-05")) - - reg := regexp.MustCompile(`[\w-]`) - result := reg.FindAllString(fileName, -1) - fileName = strings.Join(result, "") + ".html" - return strings.ToLower(fileName), nil -} - // AddKlineItem appends a SET of candles for the report to enhance upon // generation func (d *Data) AddKlineItem(k *kline.Item) { diff --git a/backtester/report/report_test.go b/backtester/report/report_test.go index 10cb6990291..b0df8c37f7b 100644 --- a/backtester/report/report_test.go +++ b/backtester/report/report_test.go @@ -2,7 +2,6 @@ package report import ( "errors" - "strings" "testing" "time" @@ -516,39 +515,3 @@ func TestUseDarkMode(t *testing.T) { t.Error("expected true") } } - -func TestGenerateFileName(t *testing.T) { - t.Parallel() - _, err := generateFileName("", "") - if !errors.Is(err, errCannotGenerateFileName) { - t.Errorf("received '%v' expected '%v'", err, errCannotGenerateFileName) - } - - _, err = generateFileName("hello", "") - if !errors.Is(err, errCannotGenerateFileName) { - t.Errorf("received '%v' expected '%v'", err, errCannotGenerateFileName) - } - - _, err = generateFileName("hello", "moto") - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - - _, err = generateFileName("", "moto") - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - - tt := time.Now().Format("2006-01-02-15-04-05") - name, err := generateFileName("......HELL0. + _", "moto") - if !errors.Is(err, nil) { - t.Errorf("received '%v' expected '%v'", err, nil) - } - if name != "hell0_-moto-"+tt+".html" { - // there's a chance that it's failing due to the second count - nameSplit := strings.Split(name, "-") - if nameSplit[0]+nameSplit[1] != "hell0_moto" { - t.Errorf("received '%v' expected '%v'", nameSplit[0]+nameSplit[1], "hell0_moto") - } - } -} diff --git a/backtester/report/report_types.go b/backtester/report/report_types.go index 63ffb3f96a6..867c6901f80 100644 --- a/backtester/report/report_types.go +++ b/backtester/report/report_types.go @@ -17,9 +17,8 @@ import ( const maxChartLimit = 1100 var ( - errNoCandles = errors.New("no candles to enhance") - errStatisticsUnset = errors.New("unable to proceed with unset Statistics property") - errCannotGenerateFileName = errors.New("cannot generate filename") + errNoCandles = errors.New("no candles to enhance") + errStatisticsUnset = errors.New("unable to proceed with unset Statistics property") ) // Handler contains all functions required to generate statistical reporting for backtesting results From c3cfec4df4e52d23337fb02656e36e8c8c2b2849 Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 30 Jun 2022 14:35:42 +1000 Subject: [PATCH 170/171] Fixes issue where linter wants to fix my spelling --- backtester/config/configbuilder/main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index cf946ec3bab..785749d373d 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -108,6 +108,7 @@ func main() { yn := quickParse(reader) if yn == y || yn == yes { var fp, wd string + extension := "strat" // nolint:misspell // its shorthand for strategy for { wd, err = os.Getwd() if err != nil { @@ -123,7 +124,7 @@ func main() { if cfg.Nickname != "" { fn += "-" + cfg.Nickname } - fn, err = common.GenerateFileName(fn, "start") + fn, err = common.GenerateFileName(fn, extension) if err != nil { log.Printf("could not write file, please try again. err: %v", err) continue @@ -131,7 +132,7 @@ func main() { fmt.Printf("Enter output file. If blank, will default to \"%v\"\n", fn) parsedFileName := quickParse(reader) if parsedFileName != "" { - fn, err = common.GenerateFileName(parsedFileName, "start") + fn, err = common.GenerateFileName(parsedFileName, extension) if err != nil { log.Printf("could not write file, please try again. err: %v", err) continue From 7548c49301fb4a7d06cb2f963c78c3cd2df1276f Mon Sep 17 00:00:00 2001 From: gloriousCode Date: Thu, 30 Jun 2022 14:55:02 +1000 Subject: [PATCH 171/171] Fixes pointers and starts --- backtester/config/configbuilder/main.go | 5 ++++- backtester/eventhandlers/strategies/base/base_types.go | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 785749d373d..456ef391a89 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -583,7 +583,6 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* } } - var d decimal.Decimal fmt.Println("Enter the currency base. eg BTC") setting.Base = currency.NewCode(quickParse(reader)) if setting.Asset == asset.Spot { @@ -591,6 +590,7 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the initial base funds. eg 0") parseNum := quickParse(reader) if parseNum != "" { + var d decimal.Decimal d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err @@ -608,6 +608,7 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the initial quote funds. eg 10000") parseNum := quickParse(reader) if parseNum != "" { + var d decimal.Decimal d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err @@ -628,6 +629,7 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the maker-fee. eg 0.001") parseNum := quickParse(reader) if parseNum != "" { + var d decimal.Decimal d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err @@ -637,6 +639,7 @@ func addCurrencySetting(reader *bufio.Reader, usingExchangeLevelFunding bool) (* fmt.Println("Enter the taker-fee. eg 0.01") parseNum = quickParse(reader) if parseNum != "" { + var d decimal.Decimal d, err = decimal.NewFromString(parseNum) if err != nil { return nil, err diff --git a/backtester/eventhandlers/strategies/base/base_types.go b/backtester/eventhandlers/strategies/base/base_types.go index 3d8e89a6d3a..ad7ecfe26b6 100644 --- a/backtester/eventhandlers/strategies/base/base_types.go +++ b/backtester/eventhandlers/strategies/base/base_types.go @@ -3,16 +3,16 @@ package base import "errors" var ( - // ErrCustomSettingsUnsupported used when custom settings are found in the start config when they shouldn't be + // ErrCustomSettingsUnsupported used when custom settings are found in the strategy config when they shouldn't be ErrCustomSettingsUnsupported = errors.New("custom settings not supported") // ErrSimultaneousProcessingNotSupported used when strategy does not support simultaneous processing - // but start config is set to use it + // but strategy config is set to use it ErrSimultaneousProcessingNotSupported = errors.New("does not support simultaneous processing and could not be loaded") // ErrSimultaneousProcessingOnly is raised when a strategy is improperly configured ErrSimultaneousProcessingOnly = errors.New("this strategy only supports simultaneous processing") - // ErrStrategyNotFound used when strategy specified in start config does not exist - ErrStrategyNotFound = errors.New("not found. Please ensure the strategy-settings field 'name' is spelled properly in your .start config") - // ErrInvalidCustomSettings used when bad custom settings are found in the start config + // ErrStrategyNotFound used when strategy specified in strategy config does not exist + ErrStrategyNotFound = errors.New("not found. Please ensure the strategy-settings field 'name' is spelled properly in your .strat config") // nolint:misspell // its shorthand for strategy + // ErrInvalidCustomSettings used when bad custom settings are found in the strategy config ErrInvalidCustomSettings = errors.New("invalid custom settings in config") // ErrTooMuchBadData used when there is too much missing data ErrTooMuchBadData = errors.New("backtesting cannot continue as there is too much invalid data. Please review your dataset")