diff --git a/.golangci.yml b/.golangci.yml index a0835d4f..2fcc3b32 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - deadline: 30s + deadline: 40s issues-exit-code: 1 tests: true skip-dirs: diff --git a/cmd/exchange_wrapper_coverage/main.go b/cmd/exchange_wrapper_coverage/main.go index f786ac6d..c8953421 100644 --- a/cmd/exchange_wrapper_coverage/main.go +++ b/cmd/exchange_wrapper_coverage/main.go @@ -116,7 +116,15 @@ func testWrappers(e exchange.IBotExchange) []string { funcs = append(funcs, "GetFundingHistory") } - _, err = e.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1000000, 10000000000, "meow") + s := &exchange.OrderSubmission{ + Pair: p, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Amount: 1000000, + Price: 10000000000, + ClientID: "meow", + } + _, err = e.SubmitOrder(s) if err == common.ErrNotYetImplemented { funcs = append(funcs, "SubmitOrder") } diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 20438447..61e4dc84 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -171,6 +171,53 @@ func disableExchange(c *cli.Context) error { return nil } +var getExchangeOTPCommand = cli.Command{ + Name: "getexchangeotp", + Usage: "gets a specific exchanges otp code", + ArgsUsage: "", + Action: getExchangeOTPCode, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to get the otp code for", + }, + }, +} + +func getExchangeOTPCode(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + cli.ShowCommandHelp(c, "getexchangeotp") + return nil + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + var exchangeName string + if c.IsSet("exchange") { + exchangeName = c.String("exchange") + } else { + exchangeName = c.Args().First() + } + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.GetExchangeOTPCode(context.Background(), + &gctrpc.GenericExchangeNameRequest{ + Exchange: exchangeName, + }, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + var getExchangeInfoCommand = cli.Command{ Name: "getexchangeinfo", Usage: "gets a specific exchanges info", diff --git a/cmd/gctcli/main.go b/cmd/gctcli/main.go index e3d758d7..fb5e39bf 100644 --- a/cmd/gctcli/main.go +++ b/cmd/gctcli/main.go @@ -81,6 +81,7 @@ func main() { getExchangesCommand, enableExchangeCommand, disableExchangeCommand, + getExchangeOTPCommand, getExchangeInfoCommand, getTickerCommand, getTickersCommand, diff --git a/cmd/otp_gen/otp_gen.go b/cmd/gen_otp/otp_gen.go similarity index 100% rename from cmd/otp_gen/otp_gen.go rename to cmd/gen_otp/otp_gen.go diff --git a/communications/base/base.go b/communications/base/base.go index cd047aea..a4ee6b09 100644 --- a/communications/base/base.go +++ b/communications/base/base.go @@ -1,56 +1,15 @@ package base import ( - "fmt" - "strings" - "sync" "time" - - "github.com/thrasher-/gocryptotrader/exchanges/assets" - "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) // global vars contain staged update data that will be sent to the communication // mediums var ( - TickerStaged map[string]map[assets.AssetType]map[string]ticker.Price - OrderbookStaged map[string]map[assets.AssetType]map[string]Orderbook - PortfolioStaged Portfolio - SettingsStaged Settings - ServiceStarted time.Time - m sync.Mutex + ServiceStarted time.Time ) -// Orderbook holds the minimal orderbook details to be sent to a communication -// medium -type Orderbook struct { - CurrencyPair string - AssetType string - TotalAsks float64 - TotalBids float64 - LastUpdated string -} - -// Ticker holds the minimal orderbook details to be sent to a communication -// medium -type Ticker struct { - CurrencyPair string - LastUpdated string -} - -// Portfolio holds the minimal portfolio details to be sent to a communication -// medium -type Portfolio struct { - ProfitLoss string -} - -// Settings holds the minimal setting details to be sent to a communication -// medium -type Settings struct { - EnabledExchanges string - EnabledCommunications string -} - // Base enforces standard variables across communication packages type Base struct { Name string @@ -61,9 +20,8 @@ type Base struct { // Event is a generalise event type type Event struct { - Type string - GainLoss string - TradeDetails string + Type string + Message string } // IsEnabled returns if the comms package has been enabled in the configuration @@ -82,83 +40,6 @@ func (b *Base) GetName() string { return b.Name } -// GetTicker returns staged ticker data -func (b *Base) GetTicker(exchangeName string) string { - m.Lock() - defer m.Unlock() - - tickerPrice, ok := TickerStaged[exchangeName] - if !ok { - return "" - } - - var tickerPrices []ticker.Price - for x := range tickerPrice { - for y := range tickerPrice[x] { - tickerPrices = append(tickerPrices, tickerPrice[x][y]) - } - } - - var packagedTickers []string - for i := range tickerPrices { - packagedTickers = append(packagedTickers, fmt.Sprintf( - "Currency Pair: %s Ask: %f, Bid: %f High: %f Last: %f Low: %f ATH: %f Volume: %f", - tickerPrices[i].Pair, - tickerPrices[i].Ask, - tickerPrices[i].Bid, - tickerPrices[i].High, - tickerPrices[i].Last, - tickerPrices[i].Low, - tickerPrices[i].PriceATH, - tickerPrices[i].Volume)) - } - return strings.Join(packagedTickers, "\n") -} - -// GetOrderbook returns staged orderbook data -func (b *Base) GetOrderbook(exchangeName string) string { - m.Lock() - defer m.Unlock() - - orderbook, ok := OrderbookStaged[exchangeName] - if !ok { - return "" - } - - var orderbooks []Orderbook - for _, x := range orderbook { - for _, y := range x { - orderbooks = append(orderbooks, y) - } - } - - var packagedOrderbooks []string - for i := range orderbooks { - packagedOrderbooks = append(packagedOrderbooks, fmt.Sprintf( - "Currency Pair: %s AssetType: %s, LastUpdated: %s TotalAsks: %f TotalBids: %f", - orderbooks[i].CurrencyPair, - orderbooks[i].AssetType, - orderbooks[i].LastUpdated, - orderbooks[i].TotalAsks, - orderbooks[i].TotalBids)) - } - return strings.Join(packagedOrderbooks, "\n") -} - -// GetPortfolio returns staged portfolio info -func (b *Base) GetPortfolio() string { - m.Lock() - defer m.Unlock() - return fmt.Sprintf("%v", PortfolioStaged) -} - -// GetSettings returns stage setting info -func (b *Base) GetSettings() string { - m.Lock() - defer m.Unlock() - return fmt.Sprintf("%v", SettingsStaged) -} - // GetStatus returns status data func (b *Base) GetStatus() string { return ` diff --git a/communications/base/base_interface.go b/communications/base/base_interface.go index 2348c9dd..2ec99f6e 100644 --- a/communications/base/base_interface.go +++ b/communications/base/base_interface.go @@ -4,9 +4,6 @@ import ( "time" "github.com/thrasher-/gocryptotrader/config" - "github.com/thrasher-/gocryptotrader/exchanges/assets" - "github.com/thrasher-/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-/gocryptotrader/exchanges/ticker" log "github.com/thrasher-/gocryptotrader/logger" ) @@ -26,10 +23,7 @@ type ICommunicate interface { // Setup sets up communication variables and intiates a connection to the // communication mediums func (c IComm) Setup() { - TickerStaged = make(map[string]map[assets.AssetType]map[string]ticker.Price) - OrderbookStaged = make(map[string]map[assets.AssetType]map[string]Orderbook) ServiceStarted = time.Now() - for i := range c { if c[i].IsEnabled() && !c[i].IsConnected() { err := c[i].Connect() @@ -46,8 +40,8 @@ func (c IComm) PushEvent(event Event) { if c[i].IsEnabled() && c[i].IsConnected() { err := c[i].PushEvent(event) if err != nil { - log.Errorf("Communications error - PushEvent() in package %s with %v", - c[i].GetName(), event) + log.Errorf("Communications error - PushEvent() in package %s with %v. Err %s", + c[i].GetName(), event, err) } } } @@ -68,42 +62,3 @@ func (c IComm) GetEnabledCommunicationMediums() { log.Warnf("Communications: No communication mediums are enabled.") } } - -// StageTickerData stages updated ticker data for the communications package -func (c IComm) StageTickerData(exchangeName string, assetType assets.AssetType, tickerPrice *ticker.Price) { - m.Lock() - defer m.Unlock() - - if _, ok := TickerStaged[exchangeName]; !ok { - TickerStaged[exchangeName] = make(map[assets.AssetType]map[string]ticker.Price) - } - - if _, ok := TickerStaged[exchangeName][assetType]; !ok { - TickerStaged[exchangeName][assetType] = make(map[string]ticker.Price) - } - - TickerStaged[exchangeName][assetType][tickerPrice.Pair.String()] = *tickerPrice -} - -// StageOrderbookData stages updated orderbook data for the communications -// package -func (c IComm) StageOrderbookData(exchangeName string, assetType assets.AssetType, ob *orderbook.Base) { - m.Lock() - defer m.Unlock() - - if _, ok := OrderbookStaged[exchangeName]; !ok { - OrderbookStaged[exchangeName] = make(map[assets.AssetType]map[string]Orderbook) - } - - if _, ok := OrderbookStaged[exchangeName][assetType]; !ok { - OrderbookStaged[exchangeName][assetType] = make(map[string]Orderbook) - } - - _, totalAsks := ob.TotalAsksAmount() - _, totalBids := ob.TotalBidsAmount() - - OrderbookStaged[exchangeName][assetType][ob.Pair.String()] = Orderbook{ - CurrencyPair: ob.Pair.String(), - TotalAsks: totalAsks, - TotalBids: totalBids} -} diff --git a/communications/base/base_test.go b/communications/base/base_test.go index 3dc5d0e7..71cb86cb 100644 --- a/communications/base/base_test.go +++ b/communications/base/base_test.go @@ -2,14 +2,10 @@ package base import ( "testing" - - "github.com/thrasher-/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) var ( b Base - i IComm ) func TestStart(t *testing.T) { @@ -39,41 +35,6 @@ func TestGetName(t *testing.T) { } } -func TestGetTicker(t *testing.T) { - v := b.GetTicker("ANX") - if v != "" { - t.Error("test failed - base GetTicker() error") - } -} - -func TestGetOrderbook(t *testing.T) { - v := b.GetOrderbook("ANX") - if v != "" { - t.Error("test failed - base GetOrderbook() error") - } -} - -func TestGetPortfolio(t *testing.T) { - v := b.GetPortfolio() - if v != "{}" { - t.Error("test failed - base GetPortfolio() error") - } -} - -func TestGetSettings(t *testing.T) { - v := b.GetSettings() - if v != "{ }" { - t.Error("test failed - base GetSettings() error") - } -} - -func TestGetStatus(t *testing.T) { - v := b.GetStatus() - if v == "" { - t.Error("test failed - base GetStatus() error") - } -} - type CommunicationProvider struct { ICommunicate @@ -166,58 +127,3 @@ func TestPushEvent(t *testing.T) { } } } - -func TestStageTickerData(t *testing.T) { - _, ok := TickerStaged["bitstamp"]["someAsset"]["BTCUSD"] - if ok { - t.Fatalf("key should not exists") - } - - price := ticker.Price{} - var i IComm - i.Setup() - - i.StageTickerData("bitstamp", "someAsset", &price) - - _, ok = TickerStaged["bitstamp"]["someAsset"][price.Pair.String()] - if !ok { - t.Fatalf("key should exists") - } -} - -func TestOrderbookData(t *testing.T) { - _, ok := OrderbookStaged["bitstamp"]["someAsset"]["someOrderbook"] - if ok { - t.Fatal("key should not exists") - } - - ob := orderbook.Base{ - Asks: []orderbook.Item{ - {Amount: 1, Price: 2, ID: 3}, - {Amount: 4, Price: 5, ID: 6}, - }, - } - var i IComm - i.Setup() - - i.StageOrderbookData("bitstamp", "someAsset", &ob) - - orderbook, ok := OrderbookStaged["bitstamp"]["someAsset"][ob.Pair.String()] - if !ok { - t.Fatal("key should exists") - } - - if ob.Pair.String() != orderbook.CurrencyPair { - t.Fatal("currency missmatched") - } - - _, totalAsks := ob.TotalAsksAmount() - if totalAsks != orderbook.TotalAsks { - t.Fatal("total asks missmatched") - } - - _, totalBids := ob.TotalBidsAmount() - if totalBids != orderbook.TotalBids { - t.Fatal("total bids missmatched") - } -} diff --git a/communications/slack/slack.go b/communications/slack/slack.go index 444ee9c6..dad5a45e 100644 --- a/communications/slack/slack.go +++ b/communications/slack/slack.go @@ -24,21 +24,13 @@ import ( const ( SlackURL = "https://slack.com/api/rtm.start" - cmdStatus = "!status" - cmdHelp = "!help" - cmdSettings = "!settings" - cmdTicker = "!ticker" - cmdPortfolio = "!portfolio" - cmdOrderbook = "!orderbook" + cmdStatus = "!status" + cmdHelp = "!help" getHelp = `GoCryptoTrader SlackBot, thank you for using this service! Current commands are: !status - Displays current working status of bot - !help - Displays help text - !settings - Displays current settings - !ticker - Displays recent ANX ticker - !portfolio - Displays portfolio data - !orderbook - Displays current ANX orderbook` + !help - Displays help text` ) // Slack starts a websocket connection and uses https://api.slack.com/rtm real @@ -58,6 +50,11 @@ type Slack struct { sync.Mutex } +// IsConnected returns whether or not the connection is connected +func (s *Slack) IsConnected() bool { + return s.Connected +} + // Setup takes in a slack configuration, sets bots target channel and // sets verification token to access workspace func (s *Slack) Setup(cfg *config.CommunicationsConfig) { @@ -70,12 +67,22 @@ func (s *Slack) Setup(cfg *config.CommunicationsConfig) { // Connect connects to the service func (s *Slack) Connect() error { - return s.NewConnection() + err := s.NewConnection() + if err != nil { + return err + } + + s.Connected = true + return nil } // PushEvent pushes an event to either a slack channel or specific client -func (s *Slack) PushEvent(base.Event) error { - return common.ErrNotYetImplemented +func (s *Slack) PushEvent(event base.Event) error { + if s.Connected { + return s.WebsocketSend("message", + fmt.Sprintf("event: %s %s", event.Type, event.Message)) + } + return nil } // BuildURL returns an appended token string with the SlackURL @@ -155,19 +162,22 @@ func (s *Slack) NewConnection() error { } if s.Verbose { - log.Debugf("%s [%s] connected to %s [%s] \nWebsocket URL: %s.\n", + log.Debugf("Slack: %s [%s] connected to %s [%s] \nWebsocket URL: %s.\n", s.Details.Self.Name, s.Details.Self.ID, s.Details.Team.Domain, s.Details.Team.ID, s.Details.URL) - log.Debugf("Slack channels: %s", s.GetChannelsString()) + log.Debugf("Slack: Public channels: %s", s.GetChannelsString()) } s.TargetChannelID, err = s.GetIDByName(s.TargetChannel) if err != nil { return err } + + log.Debugf("Slack: Target channel ID: %v [#%v]", s.TargetChannelID, + s.TargetChannel) return s.WebsocketConnect() } return errors.New("slack.go NewConnection() Already Connected") @@ -202,7 +212,6 @@ func (s *Slack) WebsocketReader() { } var data WebsocketResponse - err = common.JSONDecode(resp, &data) if err != nil { log.Error(err) @@ -240,7 +249,7 @@ func (s *Slack) WebsocketReader() { case "pong": if s.Verbose { - log.Debugf("Pong received from server") + log.Debugf("Slack: Pong received from server") } default: log.Debugf(string(resp)) @@ -255,7 +264,7 @@ func (s *Slack) handlePresenceChange(resp []byte) error { return err } if s.Verbose { - log.Debugf("Presence change. User %s [%s] changed status to %s\n", + log.Debugf("Slack: Presence change. User %s [%s] changed status to %s\n", s.GetUsernameByID(pres.User), pres.User, pres.Presence) } @@ -272,7 +281,7 @@ func (s *Slack) handleMessageResponse(resp []byte, data WebsocketResponse) error return err } if s.Verbose { - log.Debugf("Msg received by %s [%s] with text: %s\n", + log.Debugf("Slack: Message received by %s [%s] with text: %s\n", s.GetUsernameByID(msg.User), msg.User, msg.Text) } @@ -304,7 +313,7 @@ func (s *Slack) handleErrorResponse(data WebsocketResponse) error { func (s *Slack) handleHelloResponse() { if s.Verbose { - log.Debugln("Websocket connected successfully.") + log.Debugln("Slack: Websocket connected successfully.") } s.Connected = true go s.WebsocketKeepAlive() @@ -321,7 +330,7 @@ func (s *Slack) handleReconnectResponse(resp []byte) error { } s.ReconnectURL = recURL.URL if s.Verbose { - log.Debugf("Reconnect URL set to %s\n", s.ReconnectURL) + log.Debugf("Slack: Reconnect URL set to %s\n", s.ReconnectURL) } return nil } @@ -334,7 +343,7 @@ func (s *Slack) WebsocketKeepAlive() { for { <-ticker.C if err := s.WebsocketSend("ping", ""); err != nil { - log.Debugf("slack WebsocketKeepAlive() error %s", err) + log.Debugf("Slack: WebsocketKeepAlive() error %s", err) } } } @@ -353,6 +362,11 @@ func (s *Slack) WebsocketSend(eventType, text string) error { if err != nil { return err } + + if s.Verbose { + log.Debugf("Slack: Sending websocket message: %s", string(data)) + } + if s.WebsocketConn == nil { return errors.New("websocket not connected") } @@ -362,7 +376,7 @@ func (s *Slack) WebsocketSend(eventType, text string) error { // HandleMessage handles incoming messages and/or commands from slack func (s *Slack) HandleMessage(msg *Message) error { if msg == nil { - return errors.New("msg is nil") + return errors.New("slack msg is nil") } msg.Text = strings.ToLower(msg.Text) @@ -373,18 +387,6 @@ func (s *Slack) HandleMessage(msg *Message) error { case strings.Contains(msg.Text, cmdHelp): return s.WebsocketSend("message", getHelp) - case strings.Contains(msg.Text, cmdTicker): - return s.WebsocketSend("message", s.GetTicker("ANX")) - - case strings.Contains(msg.Text, cmdOrderbook): - return s.WebsocketSend("message", s.GetOrderbook("ANX")) - - case strings.Contains(msg.Text, cmdSettings): - return s.WebsocketSend("message", s.GetSettings()) - - case strings.Contains(msg.Text, cmdPortfolio): - return s.WebsocketSend("message", s.GetPortfolio()) - default: return s.WebsocketSend("message", "GoCryptoTrader SlackBot - Command Unknown!") } diff --git a/communications/slack/slack_test.go b/communications/slack/slack_test.go index fb2c5089..050cf4fb 100644 --- a/communications/slack/slack_test.go +++ b/communications/slack/slack_test.go @@ -343,7 +343,6 @@ func TestWebsocketSend(t *testing.T) { func TestHandleMessage(t *testing.T) { msg := &Message{} - err := s.HandleMessage(msg) if err == nil { t.Error("test failed - slack HandleMessage(), Sent message through nil websocket") @@ -358,24 +357,4 @@ func TestHandleMessage(t *testing.T) { if err == nil { t.Error("test failed - slack HandleMessage(), Sent message through nil websocket") } - msg.Text = cmdTicker - err = s.HandleMessage(msg) - if err == nil { - t.Error("test failed - slack HandleMessage(), Sent message through nil websocket") - } - msg.Text = cmdOrderbook - err = s.HandleMessage(msg) - if err == nil { - t.Error("test failed - slack HandleMessage(), Sent message through nil websocket") - } - msg.Text = cmdSettings - err = s.HandleMessage(msg) - if err == nil { - t.Error("test failed - slack HandleMessage(), Sent message through nil websocket") - } - msg.Text = cmdPortfolio - err = s.HandleMessage(msg) - if err == nil { - t.Error("test failed - slack HandleMessage(), Sent message through nil websocket") - } } diff --git a/communications/smsglobal/smsglobal.go b/communications/smsglobal/smsglobal.go index ad051f7b..7f21bf61 100644 --- a/communications/smsglobal/smsglobal.go +++ b/communications/smsglobal/smsglobal.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/communications/base" "github.com/thrasher-/gocryptotrader/config" + log "github.com/thrasher-/gocryptotrader/logger" ) const ( @@ -39,6 +40,7 @@ func (s *SMSGlobal) Setup(cfg *config.CommunicationsConfig) { s.Verbose = cfg.SMSGlobalConfig.Verbose s.Username = cfg.SMSGlobalConfig.Username s.Password = cfg.SMSGlobalConfig.Password + s.SendFrom = cfg.SMSGlobalConfig.From var contacts []Contact for x := range cfg.SMSGlobalConfig.Contacts { @@ -49,10 +51,19 @@ func (s *SMSGlobal) Setup(cfg *config.CommunicationsConfig) { Enabled: cfg.SMSGlobalConfig.Contacts[x].Enabled, }, ) + log.Debugf("SMSGlobal: SMS Contact: %s. Number: %s. Enabled: %v", + cfg.SMSGlobalConfig.Contacts[x].Name, + cfg.SMSGlobalConfig.Contacts[x].Number, + cfg.SMSGlobalConfig.Contacts[x].Enabled) } s.Contacts = contacts } +// IsConnected returns whether or not the connection is connected +func (s *SMSGlobal) IsConnected() bool { + return s.Connected +} + // Connect connects to the service func (s *SMSGlobal) Connect() error { s.Connected = true @@ -60,8 +71,8 @@ func (s *SMSGlobal) Connect() error { } // PushEvent pushes an event to a contact list via SMS -func (s *SMSGlobal) PushEvent(base.Event) error { - return common.ErrNotYetImplemented +func (s *SMSGlobal) PushEvent(event base.Event) error { + return s.SendMessageToAll(event.Message) } // GetEnabledContacts returns how many SMS contacts are enabled in the @@ -139,6 +150,10 @@ func (s *SMSGlobal) RemoveContact(contact Contact) error { func (s *SMSGlobal) SendMessageToAll(message string) error { for x := range s.Contacts { if s.Contacts[x].Enabled { + if s.Verbose { + log.Debugf("SMSGlobal: Sending SMS to %s. Number: %s. Message: %s [From: %s]", + s.Contacts[x].Name, s.Contacts[x].Number, message, s.SendFrom) + } err := s.SendMessage(s.Contacts[x].Number, message) if err != nil { return err diff --git a/communications/smsglobal/smsglobal_test.go b/communications/smsglobal/smsglobal_test.go index 7a379eb2..09930200 100644 --- a/communications/smsglobal/smsglobal_test.go +++ b/communications/smsglobal/smsglobal_test.go @@ -25,7 +25,7 @@ func TestConnect(t *testing.T) { func TestPushEvent(t *testing.T) { err := s.PushEvent(base.Event{}) - if err == nil { + if err != nil { t.Error("test failed - SMSGlobal PushEvent() error") } } diff --git a/communications/smtpservice/smtpservice.go b/communications/smtpservice/smtpservice.go index 7a1fb05b..042ae6f8 100644 --- a/communications/smtpservice/smtpservice.go +++ b/communications/smtpservice/smtpservice.go @@ -39,6 +39,11 @@ func (s *SMTPservice) Setup(cfg *config.CommunicationsConfig) { s.RecipientList = cfg.SMTPConfig.RecipientList } +// IsConnected returns whether or not the connection is connected +func (s *SMTPservice) IsConnected() bool { + return s.Connected +} + // Connect connects to service func (s *SMTPservice) Connect() error { s.Connected = true diff --git a/communications/telegram/telegram.go b/communications/telegram/telegram.go index 33f0d19c..11a200f0 100644 --- a/communications/telegram/telegram.go +++ b/communications/telegram/telegram.go @@ -23,23 +23,17 @@ const ( methodGetUpdates = "getUpdates" methodSendMessage = "sendMessage" - cmdStart = "/start" - cmdStatus = "/status" - cmdHelp = "/help" - cmdSettings = "/settings" - cmdTicker = "/ticker" - cmdPortfolio = "/portfolio" - cmdOrders = "/orderbooks" + cmdStart = "/start" + cmdStatus = "/status" + cmdHelp = "/help" + cmdSettings = "/settings" cmdHelpReply = `GoCryptoTrader TelegramBot, thank you for using this service! Current commands are: /start - Will authenticate your ID /status - Displays the status of the bot /help - Displays current command list - /settings - Displays current bot settings - /ticker - Displays current ANX ticker data - /portfolio - Displays your current portfolio - /orderbooks - Displays current orderbooks for ANX` + /settings - Displays current bot settings` talkRoot = "GoCryptoTrader bot" ) @@ -52,6 +46,9 @@ type Telegram struct { AuthorisedClients []int64 } +// IsConnected returns whether or not the connection is connected +func (t *Telegram) IsConnected() bool { return t.Connected } + // Setup takes in a Telegram configuration and sets verification token func (t *Telegram) Setup(cfg *config.CommunicationsConfig) { t.Name = cfg.TelegramConfig.Name @@ -73,8 +70,8 @@ func (t *Telegram) Connect() error { // PushEvent sends an event to a supplied recipient list via telegram func (t *Telegram) PushEvent(event base.Event) error { for i := range t.AuthorisedClients { - err := t.SendMessage(fmt.Sprintf("Type: %s Details: %s GainOrLoss: %s", - event.Type, event.TradeDetails, event.GainLoss), t.AuthorisedClients[i]) + err := t.SendMessage(fmt.Sprintf("Type: %s Message: %s", + event.Type, event.Message), t.AuthorisedClients[i]) if err != nil { return err } @@ -146,21 +143,9 @@ func (t *Telegram) HandleMessages(text string, chatID int64) error { case strings.Contains(text, cmdStart): return t.SendMessage(fmt.Sprintf("%s: START COMMANDS HERE", talkRoot), chatID) - case strings.Contains(text, cmdOrders): - return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetOrderbook("ANX")), chatID) - case strings.Contains(text, cmdStatus): return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetStatus()), chatID) - case strings.Contains(text, cmdTicker): - return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetTicker("ANX")), chatID) - - case strings.Contains(text, cmdSettings): - return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetSettings()), chatID) - - case strings.Contains(text, cmdPortfolio): - return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetPortfolio()), chatID) - default: return t.SendMessage(fmt.Sprintf("command %s not recognized", text), chatID) } diff --git a/communications/telegram/telegram_test.go b/communications/telegram/telegram_test.go index 277439ba..994b511d 100644 --- a/communications/telegram/telegram_test.go +++ b/communications/telegram/telegram_test.go @@ -58,31 +58,16 @@ func TestHandleMessages(t *testing.T) { t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", err) } - err = T.HandleMessages(cmdOrders, chatID) - if err.Error() != testErrNotFound { - t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", - err) - } err = T.HandleMessages(cmdStatus, chatID) if err.Error() != testErrNotFound { t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", err) } - err = T.HandleMessages(cmdTicker, chatID) - if err.Error() != testErrNotFound { - t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", - err) - } err = T.HandleMessages(cmdSettings, chatID) if err.Error() != testErrNotFound { t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", err) } - err = T.HandleMessages(cmdPortfolio, chatID) - if err.Error() != testErrNotFound { - t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", - err) - } err = T.HandleMessages("Not a command", chatID) if err.Error() != testErrNotFound { t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'", diff --git a/config/config.go b/config/config.go index 51ef8545..31a5ea7b 100644 --- a/config/config.go +++ b/config/config.go @@ -299,6 +299,7 @@ func (c *Config) CheckCommunicationsConfig() { } else { c.Communications.SMSGlobalConfig = SMSGlobalConfig{ Name: "SMSGlobal", + From: c.Name, Username: "main", Password: "test", @@ -328,6 +329,10 @@ func (c *Config) CheckCommunicationsConfig() { } } else { + if c.Communications.SMSGlobalConfig.From == "" { + c.Communications.SMSGlobalConfig.From = c.Name + } + if c.SMS != nil { // flush old SMS config c.SMS = nil @@ -931,7 +936,7 @@ func (c *Config) CheckExchangeConfigValues() error { c.Exchanges[i].Enabled = false continue } - if c.Exchanges[i].API.AuthenticatedSupport { + if c.Exchanges[i].API.AuthenticatedSupport && c.Exchanges[i].API.CredentialsValidator != nil { if c.Exchanges[i].API.CredentialsValidator.RequiresKey && (c.Exchanges[i].API.Credentials.Key == "" || c.Exchanges[i].API.Credentials.Key == DefaultAPIKey) { c.Exchanges[i].API.AuthenticatedSupport = false } diff --git a/config/config_types.go b/config/config_types.go index 9c317693..cd6b59f2 100644 --- a/config/config_types.go +++ b/config/config_types.go @@ -222,6 +222,7 @@ type SMSContact struct { // messaging and broadcast used by SMSGlobal type SMSGlobalConfig struct { Name string `json:"name"` + From string `json:"from"` Enabled bool `json:"enabled"` Verbose bool `json:"verbose"` Username string `json:"username"` diff --git a/engine/engine.go b/engine/engine.go index 28f92b5b..87c50ec1 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -7,6 +7,7 @@ import ( "os" "os/signal" "path" + "sync" "syscall" "time" @@ -40,6 +41,7 @@ type Engine struct { Settings Settings CryptocurrencyDepositAddresses map[string]map[string]string Uptime time.Time + ServicesWG sync.WaitGroup } // Vars for engine @@ -436,6 +438,8 @@ func (e *Engine) Stop() { log.Debugln("Config file saved successfully.") } } + // Wait for services to gracefully shutdown + e.ServicesWG.Wait() log.Debugln("Exiting.") log.CloseLogFile() os.Exit(0) diff --git a/engine/events/events.go b/engine/events/events.go index 9fb444d6..5b6df499 100644 --- a/engine/events/events.go +++ b/engine/events/events.go @@ -139,7 +139,10 @@ func (e *Event) ExecuteAction() bool { if action[0] == ActionSMSNotify { message := fmt.Sprintf("Event triggered: %s", e.String()) if action[1] == "ALL" { - comms.PushEvent(base.Event{TradeDetails: message}) + comms.PushEvent(base.Event{ + Type: "event", + Message: message, + }) } } } else { diff --git a/engine/helpers.go b/engine/helpers.go index 826dab3e..9b21b3f0 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -7,13 +7,16 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "errors" "fmt" "math/big" "net" "os" "path/filepath" + "strings" "time" + "github.com/pquerna/otp/totp" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency" exchange "github.com/thrasher-/gocryptotrader/exchanges" @@ -26,6 +29,21 @@ import ( "github.com/thrasher-/gocryptotrader/utils" ) +// GetOTPByExchange returns a OTP code for the desired exchange +// if it exists +func GetOTPByExchange(exchName string) (string, error) { + for x := range Bot.Config.Exchanges { + if !strings.EqualFold(Bot.Config.Exchanges[x].Name, exchName) { + continue + } + + if otpSecret := Bot.Config.Exchanges[x].API.Credentials.OTPSecret; otpSecret != "" { + return totp.GenerateCode(otpSecret, time.Now()) + } + } + return "", errors.New("exchange does not have a otpsecret stored") +} + // GetAuthAPISupportedExchanges returns a list of auth api enabled exchanges func GetAuthAPISupportedExchanges() []string { var exchanges []string diff --git a/engine/orders.go b/engine/orders.go index 60fc3bae..c6870a13 100644 --- a/engine/orders.go +++ b/engine/orders.go @@ -2,10 +2,12 @@ package engine import ( "errors" - "sync" + "fmt" "sync/atomic" "time" + "github.com/thrasher-/gocryptotrader/common" + "github.com/thrasher-/gocryptotrader/communications/base" exchange "github.com/thrasher-/gocryptotrader/exchanges" log "github.com/thrasher-/gocryptotrader/logger" ) @@ -16,9 +18,10 @@ var ( ErrOrdersAlreadyExists = errors.New("order already exists") ) -type orderStore struct { - m sync.Mutex - Orders map[string][]exchange.OrderDetail +func (o *orderStore) Get() map[string][]exchange.OrderDetail { + o.m.Lock() + defer o.m.Unlock() + return o.Orders } func (o *orderStore) exists(order *exchange.OrderDetail) bool { @@ -50,13 +53,6 @@ func (o *orderStore) Add(order *exchange.OrderDetail) error { return nil } -type orderManager struct { - started int32 - stopped int32 - shutdown chan struct{} - orderStore orderStore -} - func (o *orderManager) Started() bool { return atomic.LoadInt32(&o.started) == 1 } @@ -67,6 +63,9 @@ func (o *orderManager) Start() error { } log.Debugln("Order manager starting...") + + // test param + o.cfg.CancelOrdersOnShutdown = true o.shutdown = make(chan struct{}) o.orderStore.Orders = make(map[string][]exchange.OrderDetail) go o.run() @@ -82,17 +81,59 @@ func (o *orderManager) Stop() error { return nil } +func (o *orderManager) gracefulShutdown() { + if o.cfg.CancelOrdersOnShutdown { + log.Debug("Order manager: Cancelling any open orders...") + orders := o.orderStore.Get() + if orders == nil { + return + } + + for k, v := range orders { + log.Debugf("Order manager: Cancelling order(s) for exchange %s.", k) + for y := range v { + log.Debugf("order manager: Cancelling order ID %v [%v]", + v[y].ID, v[y]) + err := o.Cancel(k, &exchange.OrderCancellation{ + OrderID: v[y].ID, + }) + if err != nil { + msg := fmt.Sprintf("Order manager: Exchange %s unable to cancel order ID=%v. Err: %s", + k, v[y].ID, err) + log.Debugln(msg) + Bot.CommsRelayer.PushEvent(base.Event{ + Type: "order", + Message: msg, + }) + continue + } + + msg := fmt.Sprintf("Order manager: Exchange %s order ID=%v cancelled.", + k, v[y].ID) + log.Debugln(msg) + Bot.CommsRelayer.PushEvent(base.Event{ + Type: "order", + Message: msg, + }) + } + } + } +} + func (o *orderManager) run() { log.Debugln("Order manager started.") tick := time.NewTicker(OrderManagerDelay) + Bot.ServicesWG.Add(1) defer func() { log.Debugf("Order manager shutdown.") tick.Stop() + Bot.ServicesWG.Done() }() for { select { case <-o.shutdown: + o.gracefulShutdown() return case <-tick.C: o.processOrders() @@ -100,9 +141,99 @@ func (o *orderManager) run() { } } -func (o *orderManager) Cancel() {} +func (o *orderManager) CancelAllOrders() {} -func (o *orderManager) Place() {} +func (o *orderManager) Cancel(exchName string, order *exchange.OrderCancellation) error { + if exchName == "" { + return errors.New("order exchange name is empty") + } + + if order == nil { + return errors.New("order cancel param is nil") + } + + if order.OrderID == "" { + return errors.New("order id is empty") + } + + exch := GetExchangeByName(exchName) + if exch == nil { + return errors.New("unable to get exchange by name") + } + + if order.AssetType.String() != "" && !exch.GetAssetTypes().Contains(order.AssetType) { + return errors.New("order asset type not supported by exchange") + } + + return exch.CancelOrder(order) +} + +func (o *orderManager) Submit(exchName string, order *exchange.OrderSubmission) (*orderSubmitResponse, error) { + if exchName == "" { + return nil, errors.New("order exchange name must be specified") + } + + if order == nil { + return nil, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return nil, err + } + + if o.cfg.EnforceLimitConfig { + if !o.cfg.AllowMarketOrders && order.OrderType == exchange.MarketOrderType { + return nil, errors.New("order market type is not allowed") + } + + if o.cfg.LimitAmount > 0 && order.Amount > o.cfg.LimitAmount { + return nil, errors.New("order limit exceeds allowed limit") + } + + if len(o.cfg.AllowedExchanges) > 0 && + !common.StringDataCompareInsensitive(o.cfg.AllowedExchanges, exchName) { + return nil, errors.New("order exchange not found in allowed list") + } + + if len(o.cfg.AllowedPairs) > 0 && !o.cfg.AllowedPairs.Contains(order.Pair, true) { + return nil, errors.New("order pair not found in allowed list") + } + } + + exch := GetExchangeByName(exchName) + if exch == nil { + return nil, errors.New("unable to get exchange by name") + } + + id, err := common.GetV4UUID() + if err != nil { + log.Warnf("Order manager: Unable to generate UUID. Err: %s", err) + } + + result, err := exch.SubmitOrder(order) + if err != nil { + return nil, err + } + + if result.IsOrderPlaced { + return nil, errors.New("order unable to be placed") + } + + msg := fmt.Sprintf("Order manager: Exchange %s submitted order ID=%v [Ours: %v] pair=%v price=%v amount=%v side=%v type=%v.", + exchName, result.OrderID, id.String(), order.Pair, order.Price, order.Amount, order.OrderSide, order.OrderType) + log.Debugln(msg) + Bot.CommsRelayer.PushEvent(base.Event{ + Type: "order", + Message: msg, + }) + + return &orderSubmitResponse{ + SubmitOrderResponse: exchange.SubmitOrderResponse{ + OrderID: result.OrderID, + }, + OurOrderID: id.String(), + }, nil +} func (o *orderManager) processOrders() { authExchanges := GetAuthAPISupportedExchanges() @@ -123,8 +254,14 @@ func (o *orderManager) processOrders() { order := &result[x] result := o.orderStore.Add(order) if result != ErrOrdersAlreadyExists { - log.Debugf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.", + msg := fmt.Sprintf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.", order.Exchange, order.ID, order.CurrencyPair, order.Price, order.Amount, order.OrderSide, order.OrderType) + log.Debug(msg) + Bot.CommsRelayer.PushEvent(base.Event{ + Type: "order", + Message: msg, + }) + continue } } } diff --git a/engine/orders_types.go b/engine/orders_types.go new file mode 100644 index 00000000..db9c460e --- /dev/null +++ b/engine/orders_types.go @@ -0,0 +1,36 @@ +package engine + +import ( + "sync" + + "github.com/thrasher-/gocryptotrader/currency" + exchange "github.com/thrasher-/gocryptotrader/exchanges" +) + +type orderManagerConfig struct { + EnforceLimitConfig bool + AllowMarketOrders bool + CancelOrdersOnShutdown bool + LimitAmount float64 + AllowedPairs currency.Pairs + AllowedExchanges []string + OrderSubmissionRetries int64 +} + +type orderStore struct { + m sync.Mutex + Orders map[string][]exchange.OrderDetail +} + +type orderManager struct { + started int32 + stopped int32 + shutdown chan struct{} + orderStore orderStore + cfg orderManagerConfig +} + +type orderSubmitResponse struct { + exchange.SubmitOrderResponse + OurOrderID string +} diff --git a/engine/portfolio.go b/engine/portfolio.go index 36d613d7..5d91d2b7 100644 --- a/engine/portfolio.go +++ b/engine/portfolio.go @@ -48,10 +48,12 @@ func (p *portfolioManager) Stop() error { func (p *portfolioManager) run() { log.Debugln("Portfolio manager started.") + Bot.ServicesWG.Add(1) tick := time.NewTicker(PortfolioSleepDelay) defer func() { log.Debugf("Portfolio manager shutdown.") tick.Stop() + Bot.ServicesWG.Done() }() for { diff --git a/engine/routines.go b/engine/routines.go index c050a647..1a553114 100644 --- a/engine/routines.go +++ b/engine/routines.go @@ -214,7 +214,6 @@ func TickerUpdaterRoutine() { } printTickerSummary(&result, c, assetType, exchangeName, err) if err == nil { - Bot.CommsRelayer.StageTickerData(exchangeName, assetType, &result) if Bot.Config.RemoteControl.WebsocketRPC.Enabled { relayWebsocketEvent(result, "ticker_update", assetType.String(), exchangeName) } @@ -261,7 +260,6 @@ func OrderbookUpdaterRoutine() { result, err := exch.UpdateOrderbook(c, assetType) printOrderbookSummary(&result, c, assetType, exchangeName, err) if err == nil { - Bot.CommsRelayer.StageOrderbookData(exchangeName, assetType, &result) if Bot.Config.RemoteControl.WebsocketRPC.Enabled { relayWebsocketEvent(result, "orderbook_update", assetType.String(), exchangeName) } @@ -324,7 +322,7 @@ var wg sync.WaitGroup func Websocketshutdown(ws *exchange.Websocket) error { err := ws.Shutdown() // shutdown routines on the exchange if err != nil { - log.Errorf("routines.go error - failed to shutodwn %s", err) + log.Errorf("routines.go error - failed to shutdown %s", err) } timer := time.NewTimer(5 * time.Second) @@ -426,7 +424,10 @@ func WebsocketDataHandler(ws *exchange.Websocket) { Low: d.LowPrice, Volume: d.Quantity, } - Bot.ExchangeCurrencyPairManager.update(ws.GetName(), d.Pair, d.AssetType, SyncItemTicker, nil) + if Bot.Settings.EnableExchangeSyncManager && Bot.ExchangeCurrencyPairManager != nil { + Bot.ExchangeCurrencyPairManager.update(ws.GetName(), + d.Pair, d.AssetType, SyncItemTicker, nil) + } ticker.ProcessTicker(ws.GetName(), &tickerNew, d.AssetType) printTickerSummary(&tickerNew, tickerNew.Pair, d.AssetType, ws.GetName(), nil) case exchange.KlineData: @@ -437,7 +438,11 @@ func WebsocketDataHandler(ws *exchange.Websocket) { case exchange.WebsocketOrderbookUpdate: // Orderbook data result := data.(exchange.WebsocketOrderbookUpdate) - Bot.ExchangeCurrencyPairManager.update(ws.GetName(), result.Pair, result.Asset, SyncItemOrderbook, nil) + if Bot.Settings.EnableExchangeSyncManager && Bot.ExchangeCurrencyPairManager != nil { + Bot.ExchangeCurrencyPairManager.update(ws.GetName(), + result.Pair, result.Asset, SyncItemOrderbook, nil) + } + // TO-DO: printOrderbookSummary //nolint:gocritic log.Infof("Websocket %s %s orderbook updated", ws.GetName(), result.Pair.Pair().String()) default: if Bot.Settings.Verbose { diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 5131021f..c057f464 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -176,6 +176,12 @@ func (s *RPCServer) EnableExchange(ctx context.Context, r *gctrpc.GenericExchang return &gctrpc.GenericExchangeNameResponse{}, err } +// GetExchangeOTPCode retrieves an exchanges OTP code +func (s *RPCServer) GetExchangeOTPCode(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeOTPReponse, error) { + result, err := GetOTPByExchange(r.Exchange) + return &gctrpc.GetExchangeOTPReponse{OtpCode: result}, err +} + // GetExchangeInfo gets info for a specific exchange func (s *RPCServer) GetExchangeInfo(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeInfoResponse, error) { exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) @@ -580,9 +586,15 @@ func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderReques } p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) - result, err := exch.SubmitOrder(p, exchange.OrderSide(r.Side), - exchange.OrderType(r.OrderType), r.Amount, r.Price, r.ClientId) - + submission := &exchange.OrderSubmission{ + Pair: p, + OrderSide: exchange.OrderSide(r.Side), + OrderType: exchange.OrderType(r.OrderType), + Amount: r.Amount, + Price: r.Price, + ClientID: r.ClientId, + } + result, err := exch.SubmitOrder(submission) return &gctrpc.SubmitOrderResponse{ OrderId: result.OrderID, OrderPlaced: result.IsOrderPlaced, diff --git a/engine/syncer.go b/engine/syncer.go index 30178d94..0c3bf9e5 100644 --- a/engine/syncer.go +++ b/engine/syncer.go @@ -197,7 +197,7 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair return } default: - log.Warnf("ExchangeCurrencyPairSyncer: unkown sync item %v", syncType) + log.Warnf("ExchangeCurrencyPairSyncer: unknown sync item %v", syncType) return } @@ -267,10 +267,6 @@ func (e *ExchangeCurrencyPairSyncer) worker() { continue } - if Bot.Exchanges[x].GetName() == "BTCC" { - continue - } - exchangeName := Bot.Exchanges[x].GetName() assetTypes := Bot.Exchanges[x].GetAssetTypes() supportsREST := Bot.Exchanges[x].SupportsREST() @@ -456,17 +452,13 @@ func (e *ExchangeCurrencyPairSyncer) Start() { continue } - if Bot.Exchanges[x].GetName() == "BTCC" { - continue - } - exchangeName := Bot.Exchanges[x].GetName() supportsWebsocket := Bot.Exchanges[x].SupportsWebsocket() assetTypes := Bot.Exchanges[x].GetAssetTypes() supportsREST := Bot.Exchanges[x].SupportsREST() if !supportsREST && !supportsWebsocket { - log.Warnf("Loaded exchange %s does not support REST or Websocket", exchangeName) + log.Warnf("Loaded exchange %s does not support REST or Websocket.", exchangeName) continue } diff --git a/exchanges/alphapoint/alphapoint_test.go b/exchanges/alphapoint/alphapoint_test.go index 66d9b021..3065127e 100644 --- a/exchanges/alphapoint/alphapoint_test.go +++ b/exchanges/alphapoint/alphapoint_test.go @@ -536,12 +536,21 @@ func TestSubmitOrder(t *testing.T) { if areTestAPIKeysSet(a) && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "_", - Base: currency.BTC, - Quote: currency.USD, + + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := a.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + + response, err := a.SubmitOrder(orderSubmission) if !areTestAPIKeysSet(a) && err == nil { t.Error("Expecting an error when no keys are set") } diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 7d0d7be6..81d24eb1 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -183,20 +183,25 @@ func (a *Alphapoint) GetFundingHistory() ([]exchange.FundHistory, error) { // GetExchangeHistory returns historic trade data since exchange opening. func (a *Alphapoint) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ([]exchange.TradeHistory, error) { - var resp []exchange.TradeHistory - - return resp, common.ErrNotYetImplemented + return nil, common.ErrNotYetImplemented } // SubmitOrder submits a new order and returns a true value when // successfully submitted -func (a *Alphapoint) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (a *Alphapoint) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - response, err := a.CreateOrder(p.String(), - side.ToString(), - orderType.ToString(), - amount, price) + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + response, err := a.CreateOrder(order.Pair.String(), + order.OrderSide.ToString(), + order.OrderSide.ToString(), + order.Amount, order.Price) if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) diff --git a/exchanges/anx/anx_test.go b/exchanges/anx/anx_test.go index a56afc1a..fc81f78d 100644 --- a/exchanges/anx/anx_test.go +++ b/exchanges/anx/anx_test.go @@ -259,13 +259,20 @@ func TestSubmitOrder(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "_", - Base: currency.BTC, - Quote: currency.USD, + + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := a.SubmitOrder(p, - exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := a.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 79ee9209..896fca2e 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -339,26 +339,33 @@ func (a *ANX) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ([ } // SubmitOrder submits a new order -func (a *ANX) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (a *ANX) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } var isBuying bool var limitPriceInSettlementCurrency float64 - if side == exchange.BuyOrderSide { + if order.OrderSide == exchange.BuyOrderSide { isBuying = true } - if orderType == exchange.LimitOrderType { - limitPriceInSettlementCurrency = price + if order.OrderType == exchange.LimitOrderType { + limitPriceInSettlementCurrency = order.Price } - response, err := a.NewOrder(orderType.ToString(), + response, err := a.NewOrder(order.OrderType.ToString(), isBuying, - p.Base.String(), - amount, - p.Quote.String(), - amount, + order.Pair.Base.String(), + order.Amount, + order.Pair.Quote.String(), + order.Amount, limitPriceInSettlementCurrency, false, "", diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index d0ad8212..077d91b0 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -402,13 +402,20 @@ func TestSubmitOrder(t *testing.T) { b.SetDefaults() TestSetup(t) - var p = currency.Pair{ - Delimiter: "", - Base: currency.LTC, - Quote: currency.BTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 1, "clientId") + + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index ec0b8e31..ac66d9d4 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -312,18 +312,25 @@ func (b *Binance) GetExchangeHistory(p currency.Pair, assetType assets.AssetType } // SubmitOrder submits a new order -func (b *Binance) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (b *Binance) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } var sideType string - if side == exchange.BuyOrderSide { + if order.OrderSide == exchange.BuyOrderSide { sideType = exchange.BuyOrderSide.ToString() } else { sideType = exchange.SellOrderSide.ToString() } var requestParamsOrderType RequestParamsOrderType - switch orderType { + switch order.OrderType { case exchange.MarketOrderType: requestParamsOrderType = BinanceRequestParamsOrderMarket case exchange.LimitOrderType: @@ -334,10 +341,10 @@ func (b *Binance) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderTyp } var orderRequest = NewOrderRequest{ - Symbol: p.Base.String() + p.Quote.String(), + Symbol: order.Pair.Base.String() + order.Pair.Quote.String(), Side: sideType, - Price: price, - Quantity: amount, + Price: order.Price, + Quantity: order.Amount, TradeType: requestParamsOrderType, TimeInForce: BinanceRequestParamsTimeGTC, } diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index cd190774..5f975349 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -764,12 +764,19 @@ func TestSubmitOrder(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.LTC, - Quote: currency.BTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index e4424615..eff51be5 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -295,19 +295,26 @@ func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType assets.AssetTyp } // SubmitOrder submits a new order -func (b *Bitfinex) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (b *Bitfinex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var isBuying bool + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - if side == exchange.BuyOrderSide { + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var isBuying bool + if order.OrderSide == exchange.BuyOrderSide { isBuying = true } - response, err := b.NewOrder(p.String(), - amount, - price, + response, err := b.NewOrder(order.Pair.String(), + order.Amount, + order.Price, isBuying, - orderType.ToString(), + order.OrderType.ToString(), false) if response.OrderID > 0 { diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index 6eac9747..7e712f31 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -303,12 +303,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.LTC, - Quote: currency.BTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.LTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - _, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + _, err := b.SubmitOrder(orderSubmission) if err != common.ErrNotYetImplemented { t.Errorf("Expected 'Not Yet Implemented', received %v", err) } diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index 97a2339f..dc502b2e 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -277,10 +277,8 @@ func (b *Bitflyer) GetExchangeHistory(p currency.Pair, assetType assets.AssetTyp } // SubmitOrder submits a new order -func (b *Bitflyer) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { - var submitOrderResponse exchange.SubmitOrderResponse - - return submitOrderResponse, common.ErrNotYetImplemented +func (b *Bitflyer) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { + return exchange.SubmitOrderResponse{}, common.ErrNotYetImplemented } // ModifyOrder will allow of changing orderbook placement and limit to diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index 497006a2..aa8f7e02 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -345,12 +345,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.LTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.LTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 4164f8bb..3ac1b575 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -272,28 +272,34 @@ func (b *Bithumb) GetExchangeHistory(p currency.Pair, assetType assets.AssetType // SubmitOrder submits a new order // TODO: Fill this out to support limit orders -func (b *Bithumb) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, _ float64, _ string) (exchange.SubmitOrderResponse, error) { +func (b *Bithumb) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var err error + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + var orderID string - if side == exchange.BuyOrderSide { + var err error + if order.OrderSide == exchange.BuyOrderSide { var result MarketBuy - result, err = b.MarketBuyOrder(p.Base.String(), amount) + result, err = b.MarketBuyOrder(order.Pair.Base.String(), order.Amount) orderID = result.OrderID - } else if side == exchange.SellOrderSide { + } else if order.OrderSide == exchange.SellOrderSide { var result MarketSell - result, err = b.MarketSellOrder(p.Base.String(), amount) + result, err = b.MarketSellOrder(order.Pair.Base.String(), order.Amount) orderID = result.OrderID } if orderID != "" { submitOrderResponse.OrderID = fmt.Sprintf("%v", orderID) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index 3c303d9e..aca033b1 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -516,12 +516,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.XBT, - Quote: currency.USD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.XBT, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index a066d301..7368ed03 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -343,23 +343,30 @@ func (b *Bitmex) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (b *Bitmex) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (b *Bitmex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - if math.Mod(amount, 1) != 0 { + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + if math.Mod(order.Amount, 1) != 0 { return submitOrderResponse, - errors.New("contract amount can not have decimals") + errors.New("order contract amount can not have decimals") } var orderNewParams = OrderNewParams{ - OrdType: side.ToString(), - Symbol: p.String(), - OrderQty: amount, - Side: side.ToString(), + OrdType: order.OrderSide.ToString(), + Symbol: order.Pair.String(), + OrderQty: order.Amount, + Side: order.OrderSide.ToString(), } - if orderType == exchange.LimitOrderType { - orderNewParams.Price = price + if order.OrderType == exchange.LimitOrderType { + orderNewParams.Price = order.Price } response, err := b.CreateOrder(&orderNewParams) diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index c01438f8..e09c09d0 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -417,13 +417,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, - exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 973de297..a28841ab 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -308,11 +308,20 @@ func (b *Bitstamp) GetExchangeHistory(p currency.Pair, assetType assets.AssetTyp } // SubmitOrder submits a new order -func (b *Bitstamp) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (b *Bitstamp) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - buy := side == exchange.BuyOrderSide - market := orderType == exchange.MarketOrderType - response, err := b.PlaceOrder(p.String(), price, amount, buy, market) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + buy := order.OrderSide == exchange.BuyOrderSide + market := order.OrderType == exchange.MarketOrderType + response, err := b.PlaceOrder(order.Pair.String(), order.Price, order.Amount, + buy, market) if response.ID > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response.ID) diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 323f87b3..3198fe8a 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -373,12 +373,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "-", - Base: currency.BTC, - Quote: currency.LTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.LTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 9f04cddb..9d418cf5 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -288,20 +288,30 @@ func (b *Bittrex) GetExchangeHistory(p currency.Pair, assetType assets.AssetType } // SubmitOrder submits a new order -func (b *Bittrex) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (b *Bittrex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - buy := side == exchange.BuyOrderSide + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + buy := order.OrderSide == exchange.BuyOrderSide var response UUID var err error - if orderType != exchange.LimitOrderType { - return submitOrderResponse, errors.New("not supported on exchange") + if order.OrderType != exchange.LimitOrderType { + return submitOrderResponse, errors.New("limit order not supported on exchange") } if buy { - response, err = b.PlaceBuyLimit(p.String(), amount, price) + response, err = b.PlaceBuyLimit(order.Pair.String(), order.Amount, + order.Price) } else { - response, err = b.PlaceSellLimit(p.String(), amount, price) + response, err = b.PlaceSellLimit(order.Pair.String(), order.Amount, + order.Price) } if response.Result.ID != "" { diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index e7b90f71..f1d50089 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -332,12 +332,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "-", - Base: currency.BTC, - Quote: currency.LTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.LTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 2cc4b659..79d2f6a0 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -273,15 +273,30 @@ func (b *BTCMarkets) GetExchangeHistory(p currency.Pair, assetType assets.AssetT } // SubmitOrder submits a new order -func (b *BTCMarkets) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { +func (b *BTCMarkets) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - response, err := b.NewOrder(p.Base.Upper().String(), - p.Quote.Upper().String(), - price, - amount, - side.ToString(), - orderType.ToString(), - clientID) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + if strings.EqualFold(order.OrderSide.ToString(), exchange.SellOrderSide.ToString()) { + order.OrderSide = exchange.AskOrderSide + } + if strings.EqualFold(order.OrderSide.ToString(), exchange.BuyOrderSide.ToString()) { + order.OrderSide = exchange.BuyOrderSide + } + + response, err := b.NewOrder(order.Pair.Base.Upper().String(), + order.Pair.Quote.Upper().String(), + order.Price, + order.Amount, + order.OrderSide.ToString(), + order.OrderType.ToString(), + order.ClientID) if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index d46f9d74..f3c36817 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -242,12 +242,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := b.SubmitOrder(p, exchange.SellOrderSide, exchange.LimitOrderType, 0.01, 1000000, "clientId") + response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 626331ae..d9665df8 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -260,11 +260,19 @@ func (b *BTSE) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ( } // SubmitOrder submits a new order -func (b *BTSE) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { +func (b *BTSE) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var resp exchange.SubmitOrderResponse - r, err := b.CreateOrder(amount, price, side.ToString(), - orderType.ToString(), b.FormatExchangeCurrency(p, - assets.AssetTypeSpot).String(), "GTC", clientID) + if order == nil { + return resp, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return resp, err + } + + r, err := b.CreateOrder(order.Amount, order.Price, order.OrderSide.ToString(), + order.OrderType.ToString(), b.FormatExchangeCurrency(order.Pair, + assets.AssetTypeSpot).String(), "GTC", order.ClientID) if err != nil { return resp, err } diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index af15e153..8e080db2 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -470,13 +470,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "-", - Base: currency.BTC, - Quote: currency.LTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := c.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 1, "clientId") + response, err := c.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index ddcee869..5325b53e 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -285,27 +285,34 @@ func (c *CoinbasePro) GetExchangeHistory(p currency.Pair, assetType assets.Asset } // SubmitOrder submits a new order -func (c *CoinbasePro) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (c *CoinbasePro) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + var response string var err error - - switch orderType { + switch order.OrderType { case exchange.MarketOrderType: - response, err = c.PlaceMarginOrder("", - amount, - amount, - side.ToString(), - p.String(), + response, err = c.PlaceMarketOrder("", + order.Amount, + order.Amount, + order.OrderSide.ToString(), + order.Pair.String(), "") case exchange.LimitOrderType: response, err = c.PlaceLimitOrder("", - price, - amount, - side.ToString(), + order.Price, + order.Amount, + order.OrderSide.ToString(), "", "", - p.String(), + order.Pair.String(), "", false) default: diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 484646b8..30d5cbe3 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -246,12 +246,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := c.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "1234234") + response, err := c.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 33c7a6aa..a1958a6e 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -1,7 +1,6 @@ package coinut import ( - "errors" "fmt" "strconv" "strings" @@ -328,33 +327,41 @@ func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (c *COINUT) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { +func (c *COINUT) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var err error - var APIresponse interface{} - isBuyOrder := side == exchange.BuyOrderSide - clientIDInt, err := strconv.ParseUint(clientID, 0, 32) - clientIDUint := uint32(clientIDInt) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var APIresponse interface{} + isBuyOrder := order.OrderSide == exchange.BuyOrderSide + clientIDInt, err := strconv.ParseUint(order.ClientID, 0, 32) if err != nil { return submitOrderResponse, err } + + clientIDUint := uint32(clientIDInt) + // Need to get the ID of the currency sent instruments, err := c.GetInstruments() if err != nil { return submitOrderResponse, err } - currencyArray := instruments.Instruments[p.String()] + currencyArray := instruments.Instruments[order.Pair.String()] currencyID := currencyArray[0].InstID - switch orderType { + switch order.OrderType { case exchange.LimitOrderType: - APIresponse, err = c.NewOrder(currencyID, amount, price, isBuyOrder, clientIDUint) + APIresponse, err = c.NewOrder(currencyID, order.Amount, order.Price, + isBuyOrder, clientIDUint) case exchange.MarketOrderType: - APIresponse, err = c.NewOrder(currencyID, amount, 0, isBuyOrder, clientIDUint) - default: - return submitOrderResponse, errors.New("unsupported order type") + APIresponse, err = c.NewOrder(currencyID, order.Amount, 0, isBuyOrder, + clientIDUint) } switch apiResp := APIresponse.(type) { diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 23060843..3adb5b5d 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -1,14 +1,10 @@ package exchange import ( - "fmt" - "sort" - "strings" "time" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency" - "github.com/thrasher-/gocryptotrader/exchanges/assets" "github.com/thrasher-/gocryptotrader/exchanges/request" ) @@ -51,12 +47,6 @@ const ( Contact ) -// SubmitOrderResponse is what is returned after submitting an order to an exchange -type SubmitOrderResponse struct { - IsOrderPlaced bool - OrderID string -} - // FeeBuilder is the type which holds all parameters required to calculate a fee // for an exchange type FeeBuilder struct { @@ -119,80 +109,6 @@ const ( UnknownWithdrawalTypeText string = "UNKNOWN" ) -// ModifyOrder is a an order modifyer -// ModifyOrder is a an order modifyer -type ModifyOrder struct { - OrderID string - OrderType - OrderSide - Price float64 - Amount float64 - LimitPriceUpper float64 - LimitPriceLower float64 - CurrencyPair currency.Pair - - ImmediateOrCancel bool - HiddenOrder bool - FillOrKill bool - PostOnly bool -} - -// ModifyOrderResponse is an order modifying return type -type ModifyOrderResponse struct { - OrderID string -} - -// CancelAllOrdersResponse returns the status from attempting to cancel all orders on an exchagne -type CancelAllOrdersResponse struct { - OrderStatus map[string]string -} - -// OrderType enforces a standard for Ordertypes across the code base -type OrderType string - -// OrderType ...types -const ( - AnyOrderType OrderType = "ANY" - LimitOrderType OrderType = "LIMIT" - MarketOrderType OrderType = "MARKET" - ImmediateOrCancelOrderType OrderType = "IMMEDIATE_OR_CANCEL" - StopOrderType OrderType = "STOP" - TrailingStopOrderType OrderType = "TRAILINGSTOP" - UnknownOrderType OrderType = "UNKNOWN" -) - -// ToLower changes the ordertype to lower case -func (o OrderType) ToLower() OrderType { - return OrderType(strings.ToLower(string(o))) -} - -// ToString changes the ordertype to the exchange standard and returns a string -func (o OrderType) ToString() string { - return fmt.Sprintf("%v", o) -} - -// OrderSide enforces a standard for OrderSides across the code base -type OrderSide string - -// OrderSide types -const ( - AnyOrderSide OrderSide = "ANY" - BuyOrderSide OrderSide = "BUY" - SellOrderSide OrderSide = "SELL" - BidOrderSide OrderSide = "BID" - AskOrderSide OrderSide = "ASK" -) - -// ToLower changes the ordertype to lower case -func (o OrderSide) ToLower() OrderSide { - return OrderSide(strings.ToLower(string(o))) -} - -// ToString changes the ordertype to the exchange standard and returns a string -func (o OrderSide) ToString() string { - return fmt.Sprintf("%v", o) -} - // AccountInfo is a Generic type to hold each exchange's holdings in // all enabled currencies type AccountInfo struct { @@ -225,34 +141,6 @@ type TradeHistory struct { Description string } -// OrderDetail holds order detail data -type OrderDetail struct { - Exchange string - AccountID string - ID string - CurrencyPair currency.Pair - OrderSide OrderSide - OrderType OrderType - OrderDate time.Time - Status string - Price float64 - Amount float64 - ExecutedAmount float64 - RemainingAmount float64 - Fee float64 - Trades []TradeHistory -} - -// OrderCancellation type required when requesting to cancel an order -type OrderCancellation struct { - AccountID string - OrderID string - CurrencyPair currency.Pair - AssetType assets.AssetType - WalletAddress string - Side OrderSide -} - // FundHistory holds exchange funding history data type FundHistory struct { ExchangeName string @@ -402,227 +290,6 @@ type API struct { } } -// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions -type GetOrdersRequest struct { - OrderType OrderType - OrderSide OrderSide - StartTicks time.Time - EndTicks time.Time - // Currencies Empty array = all currencies. Some endpoints only support singular currency enquiries - Currencies []currency.Pair -} - -// OrderStatus defines order status types -type OrderStatus string - -// All OrderStatus types -const ( - AnyOrderStatus OrderStatus = "ANY" - NewOrderStatus OrderStatus = "NEW" - ActiveOrderStatus OrderStatus = "ACTIVE" - PartiallyFilledOrderStatus OrderStatus = "PARTIALLY_FILLED" - FilledOrderStatus OrderStatus = "FILLED" - CancelledOrderStatus OrderStatus = "CANCELED" - PendingCancelOrderStatus OrderStatus = "PENDING_CANCEL" - RejectedOrderStatus OrderStatus = "REJECTED" - ExpiredOrderStatus OrderStatus = "EXPIRED" - HiddenOrderStatus OrderStatus = "HIDDEN" - UnknownOrderStatus OrderStatus = "UNKNOWN" -) - -// FilterOrdersBySide removes any OrderDetails that don't match the orderStatus provided -func FilterOrdersBySide(orders *[]OrderDetail, orderSide OrderSide) { - if orderSide == "" || orderSide == AnyOrderSide { - return - } - - var filteredOrders []OrderDetail - for i := range *orders { - if strings.EqualFold(string((*orders)[i].OrderSide), string(orderSide)) { - filteredOrders = append(filteredOrders, (*orders)[i]) - } - } - - *orders = filteredOrders -} - -// FilterOrdersByType removes any OrderDetails that don't match the orderType provided -func FilterOrdersByType(orders *[]OrderDetail, orderType OrderType) { - if orderType == "" || orderType == AnyOrderType { - return - } - - var filteredOrders []OrderDetail - for i := range *orders { - if strings.EqualFold(string((*orders)[i].OrderType), string(orderType)) { - filteredOrders = append(filteredOrders, (*orders)[i]) - } - } - - *orders = filteredOrders -} - -// FilterOrdersByTickRange removes any OrderDetails outside of the tick range -func FilterOrdersByTickRange(orders *[]OrderDetail, startTicks, endTicks time.Time) { - if startTicks.IsZero() || endTicks.IsZero() || - startTicks.Unix() == 0 || endTicks.Unix() == 0 || endTicks.Before(startTicks) { - return - } - - var filteredOrders []OrderDetail - for i := range *orders { - if (*orders)[i].OrderDate.Unix() >= startTicks.Unix() && (*orders)[i].OrderDate.Unix() <= endTicks.Unix() { - filteredOrders = append(filteredOrders, (*orders)[i]) - } - } - - *orders = filteredOrders -} - -// FilterOrdersByCurrencies removes any OrderDetails that do not match the provided currency list -// It is forgiving in that the provided currencies can match quote or base currencies -func FilterOrdersByCurrencies(orders *[]OrderDetail, currencies []currency.Pair) { - if len(currencies) == 0 { - return - } - - var filteredOrders []OrderDetail - for i := range *orders { - matchFound := false - for _, c := range currencies { - if !matchFound && (*orders)[i].CurrencyPair.EqualIncludeReciprocal(c) { - matchFound = true - } - } - - if matchFound { - filteredOrders = append(filteredOrders, (*orders)[i]) - } - } - - *orders = filteredOrders -} - -// ByPrice used for sorting orders by price -type ByPrice []OrderDetail - -func (b ByPrice) Len() int { - return len(b) -} - -func (b ByPrice) Less(i, j int) bool { - return b[i].Price < b[j].Price -} - -func (b ByPrice) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -// SortOrdersByPrice the caller function to sort orders -func SortOrdersByPrice(orders *[]OrderDetail, reverse bool) { - if reverse { - sort.Sort(sort.Reverse(ByPrice(*orders))) - } else { - sort.Sort(ByPrice(*orders)) - } -} - -// ByOrderType used for sorting orders by order type -type ByOrderType []OrderDetail - -func (b ByOrderType) Len() int { - return len(b) -} - -func (b ByOrderType) Less(i, j int) bool { - return b[i].OrderType.ToString() < b[j].OrderType.ToString() -} - -func (b ByOrderType) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -// SortOrdersByType the caller function to sort orders -func SortOrdersByType(orders *[]OrderDetail, reverse bool) { - if reverse { - sort.Sort(sort.Reverse(ByOrderType(*orders))) - } else { - sort.Sort(ByOrderType(*orders)) - } -} - -// ByCurrency used for sorting orders by order currency -type ByCurrency []OrderDetail - -func (b ByCurrency) Len() int { - return len(b) -} - -func (b ByCurrency) Less(i, j int) bool { - return b[i].CurrencyPair.String() < b[j].CurrencyPair.String() -} - -func (b ByCurrency) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -// SortOrdersByCurrency the caller function to sort orders -func SortOrdersByCurrency(orders *[]OrderDetail, reverse bool) { - if reverse { - sort.Sort(sort.Reverse(ByCurrency(*orders))) - } else { - sort.Sort(ByCurrency(*orders)) - } -} - -// ByDate used for sorting orders by order date -type ByDate []OrderDetail - -func (b ByDate) Len() int { - return len(b) -} - -func (b ByDate) Less(i, j int) bool { - return b[i].OrderDate.Unix() < b[j].OrderDate.Unix() -} - -func (b ByDate) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -// SortOrdersByDate the caller function to sort orders -func SortOrdersByDate(orders *[]OrderDetail, reverse bool) { - if reverse { - sort.Sort(sort.Reverse(ByDate(*orders))) - } else { - sort.Sort(ByDate(*orders)) - } -} - -// ByOrderSide used for sorting orders by order side (buy sell) -type ByOrderSide []OrderDetail - -func (b ByOrderSide) Len() int { - return len(b) -} - -func (b ByOrderSide) Less(i, j int) bool { - return b[i].OrderSide.ToString() < b[j].OrderSide.ToString() -} - -func (b ByOrderSide) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -// SortOrdersBySide the caller function to sort orders -func SortOrdersBySide(orders *[]OrderDetail, reverse bool) { - if reverse { - sort.Sort(sort.Reverse(ByOrderSide(*orders))) - } else { - sort.Sort(ByOrderSide(*orders)) - } -} - // Base stores the individual exchange information type Base struct { Name string diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index dcc79153..6d6537a6 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -306,13 +306,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "_", - Base: currency.BTC, - Quote: currency.USD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := e.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 10, "1234234") + response, err := e.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index a46ffc05..0703dca5 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -298,24 +298,29 @@ func (e *EXMO) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ( } // SubmitOrder submits a new order -func (e *EXMO) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (e *EXMO) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var oT string + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - switch orderType { + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var oT string + switch order.OrderType { case exchange.LimitOrderType: return submitOrderResponse, errors.New("unsupported order type") case exchange.MarketOrderType: oT = "market_buy" - if side == exchange.SellOrderSide { + if order.OrderSide == exchange.SellOrderSide { oT = "market_sell" } - default: - return submitOrderResponse, errors.New("unsupported order type") } - response, err := e.CreateOrder(p.String(), oT, price, amount) - + response, err := e.CreateOrder(order.Pair.String(), oT, order.Price, + order.Amount) if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) } diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index 4adc7866..c4466f15 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -312,13 +312,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip() } - var p = currency.Pair{ - Delimiter: "_", - Base: currency.LTC, - Quote: currency.BTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.LTC, + Quote: currency.BTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := g.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 10, "1234234") + response, err := g.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 36786d63..a9aa82c2 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -318,25 +318,31 @@ func (g *Gateio) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) // SubmitOrder submits a new order // TODO: support multiple order types (IOC) -func (g *Gateio) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (g *Gateio) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var orderTypeFormat string + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - if side == exchange.BuyOrderSide { + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var orderTypeFormat string + if order.OrderSide == exchange.BuyOrderSide { orderTypeFormat = exchange.BuyOrderSide.ToLower().ToString() } else { orderTypeFormat = exchange.SellOrderSide.ToLower().ToString() } var spotNewOrderRequestParams = SpotNewOrderRequestParams{ - Amount: amount, - Price: price, - Symbol: p.String(), + Amount: order.Amount, + Price: order.Price, + Symbol: order.Pair.String(), Type: orderTypeFormat, } response, err := g.SpotNewOrder(spotNewOrderRequestParams) - if response.OrderNumber > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response.OrderNumber) } diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index 1078befe..162c8eec 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -409,13 +409,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "_", - Base: currency.LTC, - Quote: currency.BTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.LTC, + Quote: currency.BTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := Session[1].SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 10, "1234234") + response, err := Session[1].SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index f73ad915..ad5968b7 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -266,22 +266,27 @@ func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (g *Gemini) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (g *Gemini) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - response, err := g.NewOrder(p.String(), - amount, - price, - side.ToString(), - orderType.ToString()) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + response, err := g.NewOrder(order.Pair.String(), + order.Amount, + order.Price, + order.OrderSide.ToString(), + order.OrderType.ToString()) if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index ee1428f0..d8fad344 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -232,13 +232,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.DGD, - Quote: currency.BTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.DGD, + Quote: currency.BTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := h.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 10, "1234234") + response, err := h.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index 7eea50c1..f60982d9 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -291,22 +291,27 @@ func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (h *HitBTC) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (h *HitBTC) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - response, err := h.PlaceOrder(p.String(), - price, - amount, - strings.ToLower(orderType.ToString()), - strings.ToLower(side.ToString())) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + response, err := h.PlaceOrder(order.Pair.String(), + order.Price, + order.Amount, + strings.ToLower(order.OrderType.ToString()), + strings.ToLower(order.OrderSide.ToString())) if response.OrderNumber > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response.OrderNumber) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 0ce83171..033dbfcb 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -432,18 +432,23 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USDT, - } - accounts, err := h.GetAccounts() if err != nil { t.Fatalf("Failed to get accounts. Err: %s", err) } - response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10)) + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USDT, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: strconv.FormatInt(accounts[0].ID, 10), + } + response, err := h.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 48905574..b503dfb0 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -379,34 +379,40 @@ func (h *HUOBI) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (h *HUOBI) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { +func (h *HUOBI) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - accountID, err := strconv.ParseInt(clientID, 10, 64) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + accountID, err := strconv.ParseInt(order.ClientID, 10, 64) if err != nil { return submitOrderResponse, err } var formattedType SpotNewOrderRequestParamsType var params = SpotNewOrderRequestParams{ - Amount: amount, + Amount: order.Amount, Source: "api", - Symbol: strings.ToLower(p.String()), + Symbol: strings.ToLower(order.Pair.String()), AccountID: int(accountID), } switch { - case side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType: + case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.MarketOrderType: formattedType = SpotNewOrderRequestTypeBuyMarket - case side == exchange.SellOrderSide && orderType == exchange.MarketOrderType: + case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.MarketOrderType: formattedType = SpotNewOrderRequestTypeSellMarket - case side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType: + case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.LimitOrderType: formattedType = SpotNewOrderRequestTypeBuyLimit - params.Price = price - case side == exchange.SellOrderSide && orderType == exchange.LimitOrderType: + params.Price = order.Price + case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.LimitOrderType: formattedType = SpotNewOrderRequestTypeSellLimit - params.Price = price - default: - return submitOrderResponse, errors.New("unsupported order type") + params.Price = order.Price } params.Type = formattedType @@ -414,11 +420,9 @@ func (h *HUOBI) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/huobihadax/huobihadax_test.go b/exchanges/huobihadax/huobihadax_test.go index ea0c57d3..4e4ea485 100644 --- a/exchanges/huobihadax/huobihadax_test.go +++ b/exchanges/huobihadax/huobihadax_test.go @@ -466,18 +466,23 @@ func TestSubmitOrder(t *testing.T) { t.Skip() } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USDT, - } - accounts, err := h.GetAccounts() if err != nil { t.Fatalf("Failed to get accounts. Err: %s", err) } - response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10)) + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USDT, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: strconv.FormatInt(accounts[0].ID, 10), + } + response, err := h.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } diff --git a/exchanges/huobihadax/huobihadax_wrapper.go b/exchanges/huobihadax/huobihadax_wrapper.go index 6923b317..f2f456e9 100644 --- a/exchanges/huobihadax/huobihadax_wrapper.go +++ b/exchanges/huobihadax/huobihadax_wrapper.go @@ -340,48 +340,50 @@ func (h *HUOBIHADAX) GetExchangeHistory(p currency.Pair, assetType assets.AssetT } // SubmitOrder submits a new order -func (h *HUOBIHADAX) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) { +func (h *HUOBIHADAX) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - accountID, err := strconv.ParseInt(clientID, 0, 64) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + accountID, err := strconv.ParseInt(order.ClientID, 10, 64) if err != nil { return submitOrderResponse, err } var formattedType SpotNewOrderRequestParamsType var params = SpotNewOrderRequestParams{ - Amount: amount, + Amount: order.Amount, Source: "api", - Symbol: strings.ToLower(p.String()), + Symbol: strings.ToLower(order.Pair.String()), AccountID: int(accountID), } switch { - case side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType: + case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.MarketOrderType: formattedType = SpotNewOrderRequestTypeBuyMarket - case side == exchange.SellOrderSide && orderType == exchange.MarketOrderType: + case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.MarketOrderType: formattedType = SpotNewOrderRequestTypeSellMarket - case side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType: + case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.LimitOrderType: formattedType = SpotNewOrderRequestTypeBuyLimit - params.Price = price - case side == exchange.SellOrderSide && orderType == exchange.LimitOrderType: + params.Price = order.Price + case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.LimitOrderType: formattedType = SpotNewOrderRequestTypeSellLimit - params.Price = price - default: - return submitOrderResponse, errors.New("unsupported order type") + params.Price = order.Price } params.Type = formattedType - response, err := h.SpotNewOrder(params) - if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index 4723ab5a..e5cf96f0 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -40,7 +40,7 @@ type IBotExchange interface { FormatWithdrawPermissions() string SupportsWithdrawPermissions(permissions uint32) bool GetFundingHistory() ([]FundHistory, error) - SubmitOrder(p currency.Pair, side OrderSide, orderType OrderType, amount, price float64, clientID string) (SubmitOrderResponse, error) + SubmitOrder(order *OrderSubmission) (SubmitOrderResponse, error) ModifyOrder(action *ModifyOrder) (string, error) CancelOrder(order *OrderCancellation) error CancelAllOrders(orders *OrderCancellation) (CancelAllOrdersResponse, error) diff --git a/exchanges/itbit/itbit_test.go b/exchanges/itbit/itbit_test.go index 0c76ab13..212f1e79 100644 --- a/exchanges/itbit/itbit_test.go +++ b/exchanges/itbit/itbit_test.go @@ -303,12 +303,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USDT, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := i.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi") + response, err := i.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 328a8126..a5397099 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -273,10 +273,17 @@ func (i *ItBit) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (i *ItBit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (i *ItBit) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var wallet string + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var wallet string wallets, err := i.GetWallets(url.Values{}) if err != nil { return submitOrderResponse, err @@ -285,8 +292,8 @@ func (i *ItBit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType // Determine what wallet ID to use if there is any actual available currency to make the trade! for _, i := range wallets { for j := range i.Balances { - if i.Balances[j].Currency == p.Base.String() && - i.Balances[j].AvailableBalance >= amount { + if i.Balances[j].Currency == order.Pair.Base.String() && + i.Balances[j].AvailableBalance >= order.Amount { wallet = i.ID } } @@ -295,23 +302,21 @@ func (i *ItBit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType if wallet == "" { return submitOrderResponse, fmt.Errorf("no wallet found with currency: %s with amount >= %v", - p.Base, - amount) + order.Pair.Base, + order.Amount) } response, err := i.PlaceOrder(wallet, - side.ToString(), - orderType.ToString(), - p.Base.String(), - amount, - price, - p.String(), + order.OrderSide.ToString(), + order.OrderType.ToString(), + order.Pair.Base.String(), + order.Amount, + order.Price, + order.Pair.String(), "") - if response.ID != "" { submitOrderResponse.OrderID = response.ID } - if err == nil { submitOrderResponse.IsOrderPlaced = true } diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 4b255fbf..b6d1ed52 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -422,12 +422,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.XBT, - Quote: currency.CAD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.XBT, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := k.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi") + response, err := k.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 7a98bc28..297ef16c 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -318,27 +318,31 @@ func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) } // SubmitOrder submits a new order -func (k *Kraken) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (k *Kraken) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var args = AddOrderOptions{} + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - response, err := k.AddOrder(p.String(), - side.ToString(), - orderType.ToString(), - amount, - price, + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var args = AddOrderOptions{} + response, err := k.AddOrder(order.Pair.String(), + order.OrderSide.ToString(), + order.OrderType.ToString(), + order.Amount, + order.Price, 0, 0, &args) - if len(response.TransactionIds) > 0 { submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ") } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index 1d52981b..a6e0ce57 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -297,12 +297,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.EUR, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.EUR, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi") + response, err := l.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index 9b563996..d49333b9 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -266,19 +266,25 @@ func (l *LakeBTC) GetExchangeHistory(p currency.Pair, assetType assets.AssetType } // SubmitOrder submits a new order -func (l *LakeBTC) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (l *LakeBTC) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - isBuyOrder := side == exchange.BuyOrderSide - response, err := l.Trade(isBuyOrder, amount, price, p.Lower().String()) + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + isBuyOrder := order.OrderSide == exchange.BuyOrderSide + response, err := l.Trade(isBuyOrder, order.Amount, order.Price, + order.Pair.Lower().String()) if response.ID > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response.ID) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go index 87b0ef9f..8fcd85df 100644 --- a/exchanges/localbitcoins/localbitcoins_test.go +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -256,12 +256,18 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.EUR, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.EUR, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi") + response, err := l.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index e2f5ff64..caa93459 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -255,8 +255,16 @@ func (l *LocalBitcoins) GetExchangeHistory(p currency.Pair, assetType assets.Ass } // SubmitOrder submits a new order -func (l *LocalBitcoins) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, _ float64, _ string) (exchange.SubmitOrderResponse, error) { +func (l *LocalBitcoins) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } + + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + // These are placeholder details // TODO store a user's localbitcoin details to use here var params = AdCreate{ @@ -266,17 +274,17 @@ func (l *LocalBitcoins) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ City: "City", Location: "Location", CountryCode: "US", - Currency: p.Quote.String(), + Currency: order.Pair.Quote.String(), AccountInfo: "-", BankName: "Bank", - MSG: side.ToString(), + MSG: order.OrderSide.ToString(), SMSVerficationRequired: true, TrackMaxAmount: true, RequireTrustedByAdvertiser: true, RequireIdentification: true, OnlineProvider: "", TradeType: "", - MinAmount: int(math.Round(amount)), + MinAmount: int(math.Round(order.Amount)), } // Does not return any orderID, so create the add, then get the order diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index aa1622d8..231d9324 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -1045,13 +1045,18 @@ func TestFormatWithdrawPermissions(t *testing.T) { // TestSubmitOrder Wrapper test func TestSubmitOrder(t *testing.T) { TestSetRealOrderDefaults(t) - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.EUR, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := o.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 10, "hi") + response, err := o.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index 6eac2880..06271113 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -1815,13 +1815,18 @@ func TestFormatWithdrawPermissions(t *testing.T) { func TestSubmitOrder(t *testing.T) { TestSetRealOrderDefaults(t) t.Parallel() - var p = currency.Pair{ - Delimiter: "", - Base: currency.BTC, - Quote: currency.USDT, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Base: currency.BTC, + Quote: currency.USDT, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := o.SubmitOrder(p, exchange.BuyOrderSide, - exchange.LimitOrderType, 1, 10, "hi") + response, err := o.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index f9f1e54a..f4367e2c 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -206,25 +206,34 @@ func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType assets.AssetType } // SubmitOrder submits a new order -func (o *OKGroup) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (resp exchange.SubmitOrderResponse, err error) { - request := PlaceSpotOrderRequest{ - ClientOID: clientID, - InstrumentID: o.FormatExchangeCurrency(p, assets.AssetTypeSpot).String(), - Side: strings.ToLower(side.ToString()), - Type: strings.ToLower(orderType.ToString()), - Size: strconv.FormatFloat(amount, 'f', -1, 64), +func (o *OKGroup) SubmitOrder(order *exchange.OrderSubmission) (resp exchange.SubmitOrderResponse, err error) { + if order == nil { + return resp, exchange.ErrOrderSubmissionIsNil } - if orderType == exchange.LimitOrderType { - request.Price = strconv.FormatFloat(price, 'f', -1, 64) + + err = order.Validate() + if err != nil { + return resp, err + } + + request := PlaceSpotOrderRequest{ + ClientOID: order.ClientID, + InstrumentID: o.FormatExchangeCurrency(order.Pair, assets.AssetTypeSpot).String(), + Side: strings.ToLower(order.OrderSide.ToString()), + Type: strings.ToLower(order.OrderType.ToString()), + Size: strconv.FormatFloat(order.Amount, 'f', -1, 64), + } + if order.OrderType == exchange.LimitOrderType { + request.Price = strconv.FormatFloat(order.Price, 'f', -1, 64) } orderResponse, err := o.PlaceSpotOrder(&request) if err != nil { return } + resp.IsOrderPlaced = orderResponse.Result resp.OrderID = orderResponse.OrderID - return } diff --git a/exchanges/order_types.go b/exchanges/order_types.go new file mode 100644 index 00000000..ebfe59f5 --- /dev/null +++ b/exchanges/order_types.go @@ -0,0 +1,380 @@ +package exchange + +import ( + "errors" + "fmt" + "sort" + "strings" + "time" + + "github.com/thrasher-/gocryptotrader/currency" + "github.com/thrasher-/gocryptotrader/exchanges/assets" +) + +// vars related to orders +var ( + ErrOrderSubmissionIsNil = errors.New("order submission is nil") +) + +// OrderSubmission contains the order submission data +type OrderSubmission struct { + Pair currency.Pair + OrderSide OrderSide + OrderType OrderType + Price float64 + Amount float64 + ClientID string +} + +// Validate checks the supplied data and returns whether or not its valid +func (o *OrderSubmission) Validate() error { + if o.Pair.IsEmpty() { + return errors.New("order pair is empty") + } + + if o.OrderSide != BuyOrderSide && o.OrderSide != SellOrderSide || + o.OrderSide != BidOrderSide && o.OrderSide != AskOrderSide { + return errors.New("order side is invalid") + } + + if o.OrderType != MarketOrderType && o.OrderType != LimitOrderType { + return errors.New("order type is invalid") + } + + if o.Amount <= 0 { + return errors.New("order amount is invalid") + } + + if o.OrderType == LimitOrderType && o.Price <= 0 { + return errors.New("order price must be set if limit order type is desired") + } + + return nil +} + +// SubmitOrderResponse is what is returned after submitting an order to an exchange +type SubmitOrderResponse struct { + IsOrderPlaced bool + OrderID string +} + +// ModifyOrder is a an order modifyer +type ModifyOrder struct { + OrderID string + OrderType + OrderSide + Price float64 + Amount float64 + LimitPriceUpper float64 + LimitPriceLower float64 + CurrencyPair currency.Pair + ImmediateOrCancel bool + HiddenOrder bool + FillOrKill bool + PostOnly bool +} + +// ModifyOrderResponse is an order modifying return type +type ModifyOrderResponse struct { + OrderID string +} + +// CancelAllOrdersResponse returns the status from attempting to cancel all orders on an exchagne +type CancelAllOrdersResponse struct { + OrderStatus map[string]string +} + +// OrderType enforces a standard for Ordertypes across the code base +type OrderType string + +// OrderType ...types +const ( + AnyOrderType OrderType = "ANY" + LimitOrderType OrderType = "LIMIT" + MarketOrderType OrderType = "MARKET" + ImmediateOrCancelOrderType OrderType = "IMMEDIATE_OR_CANCEL" + StopOrderType OrderType = "STOP" + TrailingStopOrderType OrderType = "TRAILINGSTOP" + UnknownOrderType OrderType = "UNKNOWN" +) + +// ToLower changes the ordertype to lower case +func (o OrderType) ToLower() OrderType { + return OrderType(strings.ToLower(string(o))) +} + +// ToString changes the ordertype to the exchange standard and returns a string +func (o OrderType) ToString() string { + return fmt.Sprintf("%v", o) +} + +// OrderSide enforces a standard for OrderSides across the code base +type OrderSide string + +// OrderSide types +const ( + AnyOrderSide OrderSide = "ANY" + BuyOrderSide OrderSide = "BUY" + SellOrderSide OrderSide = "SELL" + BidOrderSide OrderSide = "BID" + AskOrderSide OrderSide = "ASK" +) + +// ToLower changes the ordertype to lower case +func (o OrderSide) ToLower() OrderSide { + return OrderSide(strings.ToLower(string(o))) +} + +// ToString changes the ordertype to the exchange standard and returns a string +func (o OrderSide) ToString() string { + return fmt.Sprintf("%v", o) +} + +// OrderDetail holds order detail data +type OrderDetail struct { + Exchange string + AccountID string + ID string + CurrencyPair currency.Pair + OrderSide OrderSide + OrderType OrderType + OrderDate time.Time + Status string + Price float64 + Amount float64 + ExecutedAmount float64 + RemainingAmount float64 + Fee float64 + Trades []TradeHistory +} + +// OrderCancellation type required when requesting to cancel an order +type OrderCancellation struct { + AccountID string + OrderID string + CurrencyPair currency.Pair + AssetType assets.AssetType + WalletAddress string + Side OrderSide +} + +// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions +type GetOrdersRequest struct { + OrderType OrderType + OrderSide OrderSide + StartTicks time.Time + EndTicks time.Time + // Currencies Empty array = all currencies. Some endpoints only support singular currency enquiries + Currencies []currency.Pair +} + +// OrderStatus defines order status types +type OrderStatus string + +// All OrderStatus types +const ( + AnyOrderStatus OrderStatus = "ANY" + NewOrderStatus OrderStatus = "NEW" + ActiveOrderStatus OrderStatus = "ACTIVE" + PartiallyFilledOrderStatus OrderStatus = "PARTIALLY_FILLED" + FilledOrderStatus OrderStatus = "FILLED" + CancelledOrderStatus OrderStatus = "CANCELED" + PendingCancelOrderStatus OrderStatus = "PENDING_CANCEL" + RejectedOrderStatus OrderStatus = "REJECTED" + ExpiredOrderStatus OrderStatus = "EXPIRED" + HiddenOrderStatus OrderStatus = "HIDDEN" + UnknownOrderStatus OrderStatus = "UNKNOWN" +) + +// FilterOrdersBySide removes any OrderDetails that don't match the orderStatus provided +func FilterOrdersBySide(orders *[]OrderDetail, orderSide OrderSide) { + if orderSide == "" || orderSide == AnyOrderSide { + return + } + + var filteredOrders []OrderDetail + for i := range *orders { + if strings.EqualFold(string((*orders)[i].OrderSide), string(orderSide)) { + filteredOrders = append(filteredOrders, (*orders)[i]) + } + } + + *orders = filteredOrders +} + +// FilterOrdersByType removes any OrderDetails that don't match the orderType provided +func FilterOrdersByType(orders *[]OrderDetail, orderType OrderType) { + if orderType == "" || orderType == AnyOrderType { + return + } + + var filteredOrders []OrderDetail + for i := range *orders { + if strings.EqualFold(string((*orders)[i].OrderType), string(orderType)) { + filteredOrders = append(filteredOrders, (*orders)[i]) + } + } + + *orders = filteredOrders +} + +// FilterOrdersByTickRange removes any OrderDetails outside of the tick range +func FilterOrdersByTickRange(orders *[]OrderDetail, startTicks, endTicks time.Time) { + if startTicks.IsZero() || endTicks.IsZero() || + startTicks.Unix() == 0 || endTicks.Unix() == 0 || endTicks.Before(startTicks) { + return + } + + var filteredOrders []OrderDetail + for i := range *orders { + if (*orders)[i].OrderDate.Unix() >= startTicks.Unix() && (*orders)[i].OrderDate.Unix() <= endTicks.Unix() { + filteredOrders = append(filteredOrders, (*orders)[i]) + } + } + + *orders = filteredOrders +} + +// FilterOrdersByCurrencies removes any OrderDetails that do not match the provided currency list +// It is forgiving in that the provided currencies can match quote or base currencies +func FilterOrdersByCurrencies(orders *[]OrderDetail, currencies []currency.Pair) { + if len(currencies) == 0 { + return + } + + var filteredOrders []OrderDetail + for i := range *orders { + matchFound := false + for _, c := range currencies { + if !matchFound && (*orders)[i].CurrencyPair.EqualIncludeReciprocal(c) { + matchFound = true + } + } + + if matchFound { + filteredOrders = append(filteredOrders, (*orders)[i]) + } + } + + *orders = filteredOrders +} + +// ByPrice used for sorting orders by price +type ByPrice []OrderDetail + +func (b ByPrice) Len() int { + return len(b) +} + +func (b ByPrice) Less(i, j int) bool { + return b[i].Price < b[j].Price +} + +func (b ByPrice) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByPrice the caller function to sort orders +func SortOrdersByPrice(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByPrice(*orders))) + } else { + sort.Sort(ByPrice(*orders)) + } +} + +// ByOrderType used for sorting orders by order type +type ByOrderType []OrderDetail + +func (b ByOrderType) Len() int { + return len(b) +} + +func (b ByOrderType) Less(i, j int) bool { + return b[i].OrderType.ToString() < b[j].OrderType.ToString() +} + +func (b ByOrderType) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByType the caller function to sort orders +func SortOrdersByType(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByOrderType(*orders))) + } else { + sort.Sort(ByOrderType(*orders)) + } +} + +// ByCurrency used for sorting orders by order currency +type ByCurrency []OrderDetail + +func (b ByCurrency) Len() int { + return len(b) +} + +func (b ByCurrency) Less(i, j int) bool { + return b[i].CurrencyPair.String() < b[j].CurrencyPair.String() +} + +func (b ByCurrency) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByCurrency the caller function to sort orders +func SortOrdersByCurrency(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByCurrency(*orders))) + } else { + sort.Sort(ByCurrency(*orders)) + } +} + +// ByDate used for sorting orders by order date +type ByDate []OrderDetail + +func (b ByDate) Len() int { + return len(b) +} + +func (b ByDate) Less(i, j int) bool { + return b[i].OrderDate.Unix() < b[j].OrderDate.Unix() +} + +func (b ByDate) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersByDate the caller function to sort orders +func SortOrdersByDate(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByDate(*orders))) + } else { + sort.Sort(ByDate(*orders)) + } +} + +// ByOrderSide used for sorting orders by order side (buy sell) +type ByOrderSide []OrderDetail + +func (b ByOrderSide) Len() int { + return len(b) +} + +func (b ByOrderSide) Less(i, j int) bool { + return b[i].OrderSide.ToString() < b[j].OrderSide.ToString() +} + +func (b ByOrderSide) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +// SortOrdersBySide the caller function to sort orders +func SortOrdersBySide(orders *[]OrderDetail, reverse bool) { + if reverse { + sort.Sort(sort.Reverse(ByOrderSide(*orders))) + } else { + sort.Sort(ByOrderSide(*orders)) + } +} diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index 5a8bbaed..b4122ca2 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -253,19 +253,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var pair = currency.Pair{ - Delimiter: "_", - Base: currency.BTC, - Quote: currency.LTC, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.LTC, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - - response, err := p.SubmitOrder(pair, - exchange.BuyOrderSide, - exchange.LimitOrderType, - 1, - 10, - "hi") - + response, err := p.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index 49ab7f05..341de2fc 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -302,26 +302,30 @@ func (p *Poloniex) GetExchangeHistory(currencyPair currency.Pair, assetType asse } // SubmitOrder submits a new order -func (p *Poloniex) SubmitOrder(currencyPair currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (p *Poloniex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - fillOrKill := orderType == exchange.MarketOrderType - isBuyOrder := side == exchange.BuyOrderSide + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - response, err := p.PlaceOrder(currencyPair.String(), - price, - amount, + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + fillOrKill := order.OrderType == exchange.MarketOrderType + isBuyOrder := order.OrderSide == exchange.BuyOrderSide + response, err := p.PlaceOrder(order.Pair.String(), + order.Price, + order.Amount, false, fillOrKill, isBuyOrder) - if response.OrderNumber > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response.OrderNumber) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index 56f2ee2f..7c9f15dc 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -372,12 +372,19 @@ func TestSubmitOrder(t *testing.T) { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - var pair = currency.Pair{ - Delimiter: "_", - Base: currency.BTC, - Quote: currency.USD, + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - response, err := y.SubmitOrder(pair, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi") + response, err := y.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 81a12f69..e224587c 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -281,22 +281,28 @@ func (y *Yobit) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) // SubmitOrder submits a new order // Yobit only supports limit orders -func (y *Yobit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (y *Yobit) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - if orderType != exchange.LimitOrderType { + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + if order.OrderType != exchange.LimitOrderType { return submitOrderResponse, errors.New("only limit orders are allowed") } - response, err := y.Trade(p.String(), side.ToString(), amount, price) + response, err := y.Trade(order.Pair.String(), order.OrderSide.ToString(), + order.Amount, order.Price) if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index 14a50e1a..2cde1af8 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -288,25 +288,25 @@ func areTestAPIKeysSet() bool { func TestSubmitOrder(t *testing.T) { z.SetDefaults() TestSetup(t) - if areTestAPIKeysSet() && !canManipulateRealOrders { t.Skip(fmt.Sprintf("ApiKey: %s. Can place orders: %v", z.API.Credentials.Key, canManipulateRealOrders)) } - var pair = currency.Pair{ - Delimiter: "_", - Base: currency.QTUM, - Quote: currency.USDT, + + var orderSubmission = &exchange.OrderSubmission{ + Pair: currency.Pair{ + Delimiter: "_", + Base: currency.QTUM, + Quote: currency.USD, + }, + OrderSide: exchange.BuyOrderSide, + OrderType: exchange.LimitOrderType, + Price: 1, + Amount: 1, + ClientID: "meowOrder", } - - response, err := z.SubmitOrder(pair, - exchange.BuyOrderSide, - exchange.LimitOrderType, - 1, - 10, - "hi") - + response, err := z.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) { t.Errorf("Order failed to be placed: %v", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index ad1d52e6..43ea8243 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -300,32 +300,36 @@ func (z *ZB) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ([] } // SubmitOrder submits a new order -func (z *ZB) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) { +func (z *ZB) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) { var submitOrderResponse exchange.SubmitOrderResponse - var oT SpotNewOrderRequestParamsType + if order == nil { + return submitOrderResponse, exchange.ErrOrderSubmissionIsNil + } - if side == exchange.BuyOrderSide { + if err := order.Validate(); err != nil { + return submitOrderResponse, err + } + + var oT SpotNewOrderRequestParamsType + if order.OrderSide == exchange.BuyOrderSide { oT = SpotNewOrderRequestParamsTypeBuy } else { oT = SpotNewOrderRequestParamsTypeSell } var params = SpotNewOrderRequestParams{ - Amount: amount, - Price: price, - Symbol: strings.ToLower(p.String()), + Amount: order.Amount, + Price: order.Price, + Symbol: order.Pair.Lower().String(), Type: oT, } response, err := z.SpotNewOrder(params) - if response > 0 { submitOrderResponse.OrderID = fmt.Sprintf("%v", response) } - if err == nil { submitOrderResponse.IsOrderPlaced = true } - return submitOrderResponse, err } diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index b563f77b..40bd05a3 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -9,6 +9,8 @@ import ( proto "github.com/golang/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" math "math" ) @@ -273,6 +275,45 @@ func (m *GetExchangesResponse) GetExchanges() string { return "" } +type GetExchangeOTPReponse struct { + OtpCode string `protobuf:"bytes,1,opt,name=otp_code,json=otpCode,proto3" json:"otp_code,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetExchangeOTPReponse) Reset() { *m = GetExchangeOTPReponse{} } +func (m *GetExchangeOTPReponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangeOTPReponse) ProtoMessage() {} +func (*GetExchangeOTPReponse) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{6} +} + +func (m *GetExchangeOTPReponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeOTPReponse.Unmarshal(m, b) +} +func (m *GetExchangeOTPReponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeOTPReponse.Marshal(b, m, deterministic) +} +func (m *GetExchangeOTPReponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeOTPReponse.Merge(m, src) +} +func (m *GetExchangeOTPReponse) XXX_Size() int { + return xxx_messageInfo_GetExchangeOTPReponse.Size(m) +} +func (m *GetExchangeOTPReponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeOTPReponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeOTPReponse proto.InternalMessageInfo + +func (m *GetExchangeOTPReponse) GetOtpCode() string { + if m != nil { + return m.OtpCode + } + return "" +} + type DisableExchangeRequest struct { Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -284,7 +325,7 @@ func (m *DisableExchangeRequest) Reset() { *m = DisableExchangeRequest{} func (m *DisableExchangeRequest) String() string { return proto.CompactTextString(m) } func (*DisableExchangeRequest) ProtoMessage() {} func (*DisableExchangeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{6} + return fileDescriptor_77a6da22d6a3feb1, []int{7} } func (m *DisableExchangeRequest) XXX_Unmarshal(b []byte) error { @@ -334,7 +375,7 @@ func (m *GetExchangeInfoResponse) Reset() { *m = GetExchangeInfoResponse func (m *GetExchangeInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetExchangeInfoResponse) ProtoMessage() {} func (*GetExchangeInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{7} + return fileDescriptor_77a6da22d6a3feb1, []int{8} } func (m *GetExchangeInfoResponse) XXX_Unmarshal(b []byte) error { @@ -452,7 +493,7 @@ func (m *GetTickerRequest) Reset() { *m = GetTickerRequest{} } func (m *GetTickerRequest) String() string { return proto.CompactTextString(m) } func (*GetTickerRequest) ProtoMessage() {} func (*GetTickerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{8} + return fileDescriptor_77a6da22d6a3feb1, []int{9} } func (m *GetTickerRequest) XXX_Unmarshal(b []byte) error { @@ -507,7 +548,7 @@ func (m *CurrencyPair) Reset() { *m = CurrencyPair{} } func (m *CurrencyPair) String() string { return proto.CompactTextString(m) } func (*CurrencyPair) ProtoMessage() {} func (*CurrencyPair) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{9} + return fileDescriptor_77a6da22d6a3feb1, []int{10} } func (m *CurrencyPair) XXX_Unmarshal(b []byte) error { @@ -569,7 +610,7 @@ func (m *TickerResponse) Reset() { *m = TickerResponse{} } func (m *TickerResponse) String() string { return proto.CompactTextString(m) } func (*TickerResponse) ProtoMessage() {} func (*TickerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{10} + return fileDescriptor_77a6da22d6a3feb1, []int{11} } func (m *TickerResponse) XXX_Unmarshal(b []byte) error { @@ -670,7 +711,7 @@ func (m *GetTickersRequest) Reset() { *m = GetTickersRequest{} } func (m *GetTickersRequest) String() string { return proto.CompactTextString(m) } func (*GetTickersRequest) ProtoMessage() {} func (*GetTickersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{11} + return fileDescriptor_77a6da22d6a3feb1, []int{12} } func (m *GetTickersRequest) XXX_Unmarshal(b []byte) error { @@ -703,7 +744,7 @@ func (m *Tickers) Reset() { *m = Tickers{} } func (m *Tickers) String() string { return proto.CompactTextString(m) } func (*Tickers) ProtoMessage() {} func (*Tickers) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{12} + return fileDescriptor_77a6da22d6a3feb1, []int{13} } func (m *Tickers) XXX_Unmarshal(b []byte) error { @@ -749,7 +790,7 @@ func (m *GetTickersResponse) Reset() { *m = GetTickersResponse{} } func (m *GetTickersResponse) String() string { return proto.CompactTextString(m) } func (*GetTickersResponse) ProtoMessage() {} func (*GetTickersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{13} + return fileDescriptor_77a6da22d6a3feb1, []int{14} } func (m *GetTickersResponse) XXX_Unmarshal(b []byte) error { @@ -790,7 +831,7 @@ func (m *GetOrderbookRequest) Reset() { *m = GetOrderbookRequest{} } func (m *GetOrderbookRequest) String() string { return proto.CompactTextString(m) } func (*GetOrderbookRequest) ProtoMessage() {} func (*GetOrderbookRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{14} + return fileDescriptor_77a6da22d6a3feb1, []int{15} } func (m *GetOrderbookRequest) XXX_Unmarshal(b []byte) error { @@ -845,7 +886,7 @@ func (m *OrderbookItem) Reset() { *m = OrderbookItem{} } func (m *OrderbookItem) String() string { return proto.CompactTextString(m) } func (*OrderbookItem) ProtoMessage() {} func (*OrderbookItem) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{15} + return fileDescriptor_77a6da22d6a3feb1, []int{16} } func (m *OrderbookItem) XXX_Unmarshal(b []byte) error { @@ -903,7 +944,7 @@ func (m *OrderbookResponse) Reset() { *m = OrderbookResponse{} } func (m *OrderbookResponse) String() string { return proto.CompactTextString(m) } func (*OrderbookResponse) ProtoMessage() {} func (*OrderbookResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{16} + return fileDescriptor_77a6da22d6a3feb1, []int{17} } func (m *OrderbookResponse) XXX_Unmarshal(b []byte) error { @@ -976,7 +1017,7 @@ func (m *GetOrderbooksRequest) Reset() { *m = GetOrderbooksRequest{} } func (m *GetOrderbooksRequest) String() string { return proto.CompactTextString(m) } func (*GetOrderbooksRequest) ProtoMessage() {} func (*GetOrderbooksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{17} + return fileDescriptor_77a6da22d6a3feb1, []int{18} } func (m *GetOrderbooksRequest) XXX_Unmarshal(b []byte) error { @@ -1009,7 +1050,7 @@ func (m *Orderbooks) Reset() { *m = Orderbooks{} } func (m *Orderbooks) String() string { return proto.CompactTextString(m) } func (*Orderbooks) ProtoMessage() {} func (*Orderbooks) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{18} + return fileDescriptor_77a6da22d6a3feb1, []int{19} } func (m *Orderbooks) XXX_Unmarshal(b []byte) error { @@ -1055,7 +1096,7 @@ func (m *GetOrderbooksResponse) Reset() { *m = GetOrderbooksResponse{} } func (m *GetOrderbooksResponse) String() string { return proto.CompactTextString(m) } func (*GetOrderbooksResponse) ProtoMessage() {} func (*GetOrderbooksResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{19} + return fileDescriptor_77a6da22d6a3feb1, []int{20} } func (m *GetOrderbooksResponse) XXX_Unmarshal(b []byte) error { @@ -1094,7 +1135,7 @@ func (m *GetAccountInfoRequest) Reset() { *m = GetAccountInfoRequest{} } func (m *GetAccountInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetAccountInfoRequest) ProtoMessage() {} func (*GetAccountInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{20} + return fileDescriptor_77a6da22d6a3feb1, []int{21} } func (m *GetAccountInfoRequest) XXX_Unmarshal(b []byte) error { @@ -1134,7 +1175,7 @@ func (m *Account) Reset() { *m = Account{} } func (m *Account) String() string { return proto.CompactTextString(m) } func (*Account) ProtoMessage() {} func (*Account) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{21} + return fileDescriptor_77a6da22d6a3feb1, []int{22} } func (m *Account) XXX_Unmarshal(b []byte) error { @@ -1182,7 +1223,7 @@ func (m *AccountCurrencyInfo) Reset() { *m = AccountCurrencyInfo{} } func (m *AccountCurrencyInfo) String() string { return proto.CompactTextString(m) } func (*AccountCurrencyInfo) ProtoMessage() {} func (*AccountCurrencyInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{22} + return fileDescriptor_77a6da22d6a3feb1, []int{23} } func (m *AccountCurrencyInfo) XXX_Unmarshal(b []byte) error { @@ -1236,7 +1277,7 @@ func (m *GetAccountInfoResponse) Reset() { *m = GetAccountInfoResponse{} func (m *GetAccountInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetAccountInfoResponse) ProtoMessage() {} func (*GetAccountInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{23} + return fileDescriptor_77a6da22d6a3feb1, []int{24} } func (m *GetAccountInfoResponse) XXX_Unmarshal(b []byte) error { @@ -1281,7 +1322,7 @@ func (m *GetConfigRequest) Reset() { *m = GetConfigRequest{} } func (m *GetConfigRequest) String() string { return proto.CompactTextString(m) } func (*GetConfigRequest) ProtoMessage() {} func (*GetConfigRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{24} + return fileDescriptor_77a6da22d6a3feb1, []int{25} } func (m *GetConfigRequest) XXX_Unmarshal(b []byte) error { @@ -1313,7 +1354,7 @@ func (m *GetConfigResponse) Reset() { *m = GetConfigResponse{} } func (m *GetConfigResponse) String() string { return proto.CompactTextString(m) } func (*GetConfigResponse) ProtoMessage() {} func (*GetConfigResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{25} + return fileDescriptor_77a6da22d6a3feb1, []int{26} } func (m *GetConfigResponse) XXX_Unmarshal(b []byte) error { @@ -1355,7 +1396,7 @@ func (m *PortfolioAddress) Reset() { *m = PortfolioAddress{} } func (m *PortfolioAddress) String() string { return proto.CompactTextString(m) } func (*PortfolioAddress) ProtoMessage() {} func (*PortfolioAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{26} + return fileDescriptor_77a6da22d6a3feb1, []int{27} } func (m *PortfolioAddress) XXX_Unmarshal(b []byte) error { @@ -1414,7 +1455,7 @@ func (m *GetPortfolioRequest) Reset() { *m = GetPortfolioRequest{} } func (m *GetPortfolioRequest) String() string { return proto.CompactTextString(m) } func (*GetPortfolioRequest) ProtoMessage() {} func (*GetPortfolioRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{27} + return fileDescriptor_77a6da22d6a3feb1, []int{28} } func (m *GetPortfolioRequest) XXX_Unmarshal(b []byte) error { @@ -1446,7 +1487,7 @@ func (m *GetPortfolioResponse) Reset() { *m = GetPortfolioResponse{} } func (m *GetPortfolioResponse) String() string { return proto.CompactTextString(m) } func (*GetPortfolioResponse) ProtoMessage() {} func (*GetPortfolioResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{28} + return fileDescriptor_77a6da22d6a3feb1, []int{29} } func (m *GetPortfolioResponse) XXX_Unmarshal(b []byte) error { @@ -1484,7 +1525,7 @@ func (m *GetPortfolioSummaryRequest) Reset() { *m = GetPortfolioSummaryR func (m *GetPortfolioSummaryRequest) String() string { return proto.CompactTextString(m) } func (*GetPortfolioSummaryRequest) ProtoMessage() {} func (*GetPortfolioSummaryRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{29} + return fileDescriptor_77a6da22d6a3feb1, []int{30} } func (m *GetPortfolioSummaryRequest) XXX_Unmarshal(b []byte) error { @@ -1519,7 +1560,7 @@ func (m *Coin) Reset() { *m = Coin{} } func (m *Coin) String() string { return proto.CompactTextString(m) } func (*Coin) ProtoMessage() {} func (*Coin) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{30} + return fileDescriptor_77a6da22d6a3feb1, []int{31} } func (m *Coin) XXX_Unmarshal(b []byte) error { @@ -1581,7 +1622,7 @@ func (m *OfflineCoinSummary) Reset() { *m = OfflineCoinSummary{} } func (m *OfflineCoinSummary) String() string { return proto.CompactTextString(m) } func (*OfflineCoinSummary) ProtoMessage() {} func (*OfflineCoinSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{31} + return fileDescriptor_77a6da22d6a3feb1, []int{32} } func (m *OfflineCoinSummary) XXX_Unmarshal(b []byte) error { @@ -1635,7 +1676,7 @@ func (m *OnlineCoinSummary) Reset() { *m = OnlineCoinSummary{} } func (m *OnlineCoinSummary) String() string { return proto.CompactTextString(m) } func (*OnlineCoinSummary) ProtoMessage() {} func (*OnlineCoinSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{32} + return fileDescriptor_77a6da22d6a3feb1, []int{33} } func (m *OnlineCoinSummary) XXX_Unmarshal(b []byte) error { @@ -1681,7 +1722,7 @@ func (m *OfflineCoins) Reset() { *m = OfflineCoins{} } func (m *OfflineCoins) String() string { return proto.CompactTextString(m) } func (*OfflineCoins) ProtoMessage() {} func (*OfflineCoins) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{33} + return fileDescriptor_77a6da22d6a3feb1, []int{34} } func (m *OfflineCoins) XXX_Unmarshal(b []byte) error { @@ -1720,7 +1761,7 @@ func (m *OnlineCoins) Reset() { *m = OnlineCoins{} } func (m *OnlineCoins) String() string { return proto.CompactTextString(m) } func (*OnlineCoins) ProtoMessage() {} func (*OnlineCoins) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{34} + return fileDescriptor_77a6da22d6a3feb1, []int{35} } func (m *OnlineCoins) XXX_Unmarshal(b []byte) error { @@ -1763,7 +1804,7 @@ func (m *GetPortfolioSummaryResponse) Reset() { *m = GetPortfolioSummary func (m *GetPortfolioSummaryResponse) String() string { return proto.CompactTextString(m) } func (*GetPortfolioSummaryResponse) ProtoMessage() {} func (*GetPortfolioSummaryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{35} + return fileDescriptor_77a6da22d6a3feb1, []int{36} } func (m *GetPortfolioSummaryResponse) XXX_Unmarshal(b []byte) error { @@ -1833,7 +1874,7 @@ func (m *AddPortfolioAddressRequest) Reset() { *m = AddPortfolioAddressR func (m *AddPortfolioAddressRequest) String() string { return proto.CompactTextString(m) } func (*AddPortfolioAddressRequest) ProtoMessage() {} func (*AddPortfolioAddressRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{36} + return fileDescriptor_77a6da22d6a3feb1, []int{37} } func (m *AddPortfolioAddressRequest) XXX_Unmarshal(b []byte) error { @@ -1892,7 +1933,7 @@ func (m *AddPortfolioAddressResponse) Reset() { *m = AddPortfolioAddress func (m *AddPortfolioAddressResponse) String() string { return proto.CompactTextString(m) } func (*AddPortfolioAddressResponse) ProtoMessage() {} func (*AddPortfolioAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{37} + return fileDescriptor_77a6da22d6a3feb1, []int{38} } func (m *AddPortfolioAddressResponse) XXX_Unmarshal(b []byte) error { @@ -1926,7 +1967,7 @@ func (m *RemovePortfolioAddressRequest) Reset() { *m = RemovePortfolioAd func (m *RemovePortfolioAddressRequest) String() string { return proto.CompactTextString(m) } func (*RemovePortfolioAddressRequest) ProtoMessage() {} func (*RemovePortfolioAddressRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{38} + return fileDescriptor_77a6da22d6a3feb1, []int{39} } func (m *RemovePortfolioAddressRequest) XXX_Unmarshal(b []byte) error { @@ -1978,7 +2019,7 @@ func (m *RemovePortfolioAddressResponse) Reset() { *m = RemovePortfolioA func (m *RemovePortfolioAddressResponse) String() string { return proto.CompactTextString(m) } func (*RemovePortfolioAddressResponse) ProtoMessage() {} func (*RemovePortfolioAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{39} + return fileDescriptor_77a6da22d6a3feb1, []int{40} } func (m *RemovePortfolioAddressResponse) XXX_Unmarshal(b []byte) error { @@ -2009,7 +2050,7 @@ func (m *GetForexProvidersRequest) Reset() { *m = GetForexProvidersReque func (m *GetForexProvidersRequest) String() string { return proto.CompactTextString(m) } func (*GetForexProvidersRequest) ProtoMessage() {} func (*GetForexProvidersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{40} + return fileDescriptor_77a6da22d6a3feb1, []int{41} } func (m *GetForexProvidersRequest) XXX_Unmarshal(b []byte) error { @@ -2047,7 +2088,7 @@ func (m *ForexProvider) Reset() { *m = ForexProvider{} } func (m *ForexProvider) String() string { return proto.CompactTextString(m) } func (*ForexProvider) ProtoMessage() {} func (*ForexProvider) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{41} + return fileDescriptor_77a6da22d6a3feb1, []int{42} } func (m *ForexProvider) XXX_Unmarshal(b []byte) error { @@ -2128,7 +2169,7 @@ func (m *GetForexProvidersResponse) Reset() { *m = GetForexProvidersResp func (m *GetForexProvidersResponse) String() string { return proto.CompactTextString(m) } func (*GetForexProvidersResponse) ProtoMessage() {} func (*GetForexProvidersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{42} + return fileDescriptor_77a6da22d6a3feb1, []int{43} } func (m *GetForexProvidersResponse) XXX_Unmarshal(b []byte) error { @@ -2166,7 +2207,7 @@ func (m *GetForexRatesRequest) Reset() { *m = GetForexRatesRequest{} } func (m *GetForexRatesRequest) String() string { return proto.CompactTextString(m) } func (*GetForexRatesRequest) ProtoMessage() {} func (*GetForexRatesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{43} + return fileDescriptor_77a6da22d6a3feb1, []int{44} } func (m *GetForexRatesRequest) XXX_Unmarshal(b []byte) error { @@ -2201,7 +2242,7 @@ func (m *ForexRatesConversion) Reset() { *m = ForexRatesConversion{} } func (m *ForexRatesConversion) String() string { return proto.CompactTextString(m) } func (*ForexRatesConversion) ProtoMessage() {} func (*ForexRatesConversion) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{44} + return fileDescriptor_77a6da22d6a3feb1, []int{45} } func (m *ForexRatesConversion) XXX_Unmarshal(b []byte) error { @@ -2261,7 +2302,7 @@ func (m *GetForexRatesResponse) Reset() { *m = GetForexRatesResponse{} } func (m *GetForexRatesResponse) String() string { return proto.CompactTextString(m) } func (*GetForexRatesResponse) ProtoMessage() {} func (*GetForexRatesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{45} + return fileDescriptor_77a6da22d6a3feb1, []int{46} } func (m *GetForexRatesResponse) XXX_Unmarshal(b []byte) error { @@ -2311,7 +2352,7 @@ func (m *OrderDetails) Reset() { *m = OrderDetails{} } func (m *OrderDetails) String() string { return proto.CompactTextString(m) } func (*OrderDetails) ProtoMessage() {} func (*OrderDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{46} + return fileDescriptor_77a6da22d6a3feb1, []int{47} } func (m *OrderDetails) XXX_Unmarshal(b []byte) error { @@ -2429,7 +2470,7 @@ func (m *GetOrdersRequest) Reset() { *m = GetOrdersRequest{} } func (m *GetOrdersRequest) String() string { return proto.CompactTextString(m) } func (*GetOrdersRequest) ProtoMessage() {} func (*GetOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{47} + return fileDescriptor_77a6da22d6a3feb1, []int{48} } func (m *GetOrdersRequest) XXX_Unmarshal(b []byte) error { @@ -2482,7 +2523,7 @@ func (m *GetOrdersResponse) Reset() { *m = GetOrdersResponse{} } func (m *GetOrdersResponse) String() string { return proto.CompactTextString(m) } func (*GetOrdersResponse) ProtoMessage() {} func (*GetOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{48} + return fileDescriptor_77a6da22d6a3feb1, []int{49} } func (m *GetOrdersResponse) XXX_Unmarshal(b []byte) error { @@ -2522,7 +2563,7 @@ func (m *GetOrderRequest) Reset() { *m = GetOrderRequest{} } func (m *GetOrderRequest) String() string { return proto.CompactTextString(m) } func (*GetOrderRequest) ProtoMessage() {} func (*GetOrderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{49} + return fileDescriptor_77a6da22d6a3feb1, []int{50} } func (m *GetOrderRequest) XXX_Unmarshal(b []byte) error { @@ -2574,7 +2615,7 @@ func (m *SubmitOrderRequest) Reset() { *m = SubmitOrderRequest{} } func (m *SubmitOrderRequest) String() string { return proto.CompactTextString(m) } func (*SubmitOrderRequest) ProtoMessage() {} func (*SubmitOrderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{50} + return fileDescriptor_77a6da22d6a3feb1, []int{51} } func (m *SubmitOrderRequest) XXX_Unmarshal(b []byte) error { @@ -2656,7 +2697,7 @@ func (m *SubmitOrderResponse) Reset() { *m = SubmitOrderResponse{} } func (m *SubmitOrderResponse) String() string { return proto.CompactTextString(m) } func (*SubmitOrderResponse) ProtoMessage() {} func (*SubmitOrderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{51} + return fileDescriptor_77a6da22d6a3feb1, []int{52} } func (m *SubmitOrderResponse) XXX_Unmarshal(b []byte) error { @@ -2708,7 +2749,7 @@ func (m *CancelOrderRequest) Reset() { *m = CancelOrderRequest{} } func (m *CancelOrderRequest) String() string { return proto.CompactTextString(m) } func (*CancelOrderRequest) ProtoMessage() {} func (*CancelOrderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{52} + return fileDescriptor_77a6da22d6a3feb1, []int{53} } func (m *CancelOrderRequest) XXX_Unmarshal(b []byte) error { @@ -2788,7 +2829,7 @@ func (m *CancelOrderResponse) Reset() { *m = CancelOrderResponse{} } func (m *CancelOrderResponse) String() string { return proto.CompactTextString(m) } func (*CancelOrderResponse) ProtoMessage() {} func (*CancelOrderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{53} + return fileDescriptor_77a6da22d6a3feb1, []int{54} } func (m *CancelOrderResponse) XXX_Unmarshal(b []byte) error { @@ -2820,7 +2861,7 @@ func (m *CancelAllOrdersRequest) Reset() { *m = CancelAllOrdersRequest{} func (m *CancelAllOrdersRequest) String() string { return proto.CompactTextString(m) } func (*CancelAllOrdersRequest) ProtoMessage() {} func (*CancelAllOrdersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{54} + return fileDescriptor_77a6da22d6a3feb1, []int{55} } func (m *CancelAllOrdersRequest) XXX_Unmarshal(b []byte) error { @@ -2859,7 +2900,7 @@ func (m *CancelAllOrdersResponse) Reset() { *m = CancelAllOrdersResponse func (m *CancelAllOrdersResponse) String() string { return proto.CompactTextString(m) } func (*CancelAllOrdersResponse) ProtoMessage() {} func (*CancelAllOrdersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{55} + return fileDescriptor_77a6da22d6a3feb1, []int{56} } func (m *CancelAllOrdersResponse) XXX_Unmarshal(b []byte) error { @@ -2899,7 +2940,7 @@ func (m *CancelAllOrdersResponse_Orders) Reset() { *m = CancelAllOrdersR func (m *CancelAllOrdersResponse_Orders) String() string { return proto.CompactTextString(m) } func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} func (*CancelAllOrdersResponse_Orders) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{55, 0} + return fileDescriptor_77a6da22d6a3feb1, []int{56, 0} } func (m *CancelAllOrdersResponse_Orders) XXX_Unmarshal(b []byte) error { @@ -2944,7 +2985,7 @@ func (m *GetEventsRequest) Reset() { *m = GetEventsRequest{} } func (m *GetEventsRequest) String() string { return proto.CompactTextString(m) } func (*GetEventsRequest) ProtoMessage() {} func (*GetEventsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{56} + return fileDescriptor_77a6da22d6a3feb1, []int{57} } func (m *GetEventsRequest) XXX_Unmarshal(b []byte) error { @@ -2980,7 +3021,7 @@ func (m *ConditionParams) Reset() { *m = ConditionParams{} } func (m *ConditionParams) String() string { return proto.CompactTextString(m) } func (*ConditionParams) ProtoMessage() {} func (*ConditionParams) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{57} + return fileDescriptor_77a6da22d6a3feb1, []int{58} } func (m *ConditionParams) XXX_Unmarshal(b []byte) error { @@ -3053,7 +3094,7 @@ func (m *GetEventsResponse) Reset() { *m = GetEventsResponse{} } func (m *GetEventsResponse) String() string { return proto.CompactTextString(m) } func (*GetEventsResponse) ProtoMessage() {} func (*GetEventsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{58} + return fileDescriptor_77a6da22d6a3feb1, []int{59} } func (m *GetEventsResponse) XXX_Unmarshal(b []byte) error { @@ -3139,7 +3180,7 @@ func (m *AddEventRequest) Reset() { *m = AddEventRequest{} } func (m *AddEventRequest) String() string { return proto.CompactTextString(m) } func (*AddEventRequest) ProtoMessage() {} func (*AddEventRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{59} + return fileDescriptor_77a6da22d6a3feb1, []int{60} } func (m *AddEventRequest) XXX_Unmarshal(b []byte) error { @@ -3213,7 +3254,7 @@ func (m *AddEventResponse) Reset() { *m = AddEventResponse{} } func (m *AddEventResponse) String() string { return proto.CompactTextString(m) } func (*AddEventResponse) ProtoMessage() {} func (*AddEventResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{60} + return fileDescriptor_77a6da22d6a3feb1, []int{61} } func (m *AddEventResponse) XXX_Unmarshal(b []byte) error { @@ -3252,7 +3293,7 @@ func (m *RemoveEventRequest) Reset() { *m = RemoveEventRequest{} } func (m *RemoveEventRequest) String() string { return proto.CompactTextString(m) } func (*RemoveEventRequest) ProtoMessage() {} func (*RemoveEventRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{61} + return fileDescriptor_77a6da22d6a3feb1, []int{62} } func (m *RemoveEventRequest) XXX_Unmarshal(b []byte) error { @@ -3290,7 +3331,7 @@ func (m *RemoveEventResponse) Reset() { *m = RemoveEventResponse{} } func (m *RemoveEventResponse) String() string { return proto.CompactTextString(m) } func (*RemoveEventResponse) ProtoMessage() {} func (*RemoveEventResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{62} + return fileDescriptor_77a6da22d6a3feb1, []int{63} } func (m *RemoveEventResponse) XXX_Unmarshal(b []byte) error { @@ -3324,7 +3365,7 @@ func (m *GetCryptocurrencyDepositAddressesRequest) Reset() { func (m *GetCryptocurrencyDepositAddressesRequest) String() string { return proto.CompactTextString(m) } func (*GetCryptocurrencyDepositAddressesRequest) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{63} + return fileDescriptor_77a6da22d6a3feb1, []int{64} } func (m *GetCryptocurrencyDepositAddressesRequest) XXX_Unmarshal(b []byte) error { @@ -3365,7 +3406,7 @@ func (m *GetCryptocurrencyDepositAddressesResponse) Reset() { func (m *GetCryptocurrencyDepositAddressesResponse) String() string { return proto.CompactTextString(m) } func (*GetCryptocurrencyDepositAddressesResponse) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{64} + return fileDescriptor_77a6da22d6a3feb1, []int{65} } func (m *GetCryptocurrencyDepositAddressesResponse) XXX_Unmarshal(b []byte) error { @@ -3407,7 +3448,7 @@ func (m *GetCryptocurrencyDepositAddressRequest) Reset() { func (m *GetCryptocurrencyDepositAddressRequest) String() string { return proto.CompactTextString(m) } func (*GetCryptocurrencyDepositAddressRequest) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{65} + return fileDescriptor_77a6da22d6a3feb1, []int{66} } func (m *GetCryptocurrencyDepositAddressRequest) XXX_Unmarshal(b []byte) error { @@ -3455,7 +3496,7 @@ func (m *GetCryptocurrencyDepositAddressResponse) Reset() { func (m *GetCryptocurrencyDepositAddressResponse) String() string { return proto.CompactTextString(m) } func (*GetCryptocurrencyDepositAddressResponse) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{66} + return fileDescriptor_77a6da22d6a3feb1, []int{67} } func (m *GetCryptocurrencyDepositAddressResponse) XXX_Unmarshal(b []byte) error { @@ -3510,7 +3551,7 @@ func (m *WithdrawCurrencyRequest) Reset() { *m = WithdrawCurrencyRequest func (m *WithdrawCurrencyRequest) String() string { return proto.CompactTextString(m) } func (*WithdrawCurrencyRequest) ProtoMessage() {} func (*WithdrawCurrencyRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{67} + return fileDescriptor_77a6da22d6a3feb1, []int{68} } func (m *WithdrawCurrencyRequest) XXX_Unmarshal(b []byte) error { @@ -3661,7 +3702,7 @@ func (m *WithdrawResponse) Reset() { *m = WithdrawResponse{} } func (m *WithdrawResponse) String() string { return proto.CompactTextString(m) } func (*WithdrawResponse) ProtoMessage() {} func (*WithdrawResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{68} + return fileDescriptor_77a6da22d6a3feb1, []int{69} } func (m *WithdrawResponse) XXX_Unmarshal(b []byte) error { @@ -3696,6 +3737,7 @@ func init() { proto.RegisterType((*GenericExchangeNameResponse)(nil), "gctrpc.GenericExchangeNameResponse") proto.RegisterType((*GetExchangesRequest)(nil), "gctrpc.GetExchangesRequest") proto.RegisterType((*GetExchangesResponse)(nil), "gctrpc.GetExchangesResponse") + proto.RegisterType((*GetExchangeOTPReponse)(nil), "gctrpc.GetExchangeOTPReponse") proto.RegisterType((*DisableExchangeRequest)(nil), "gctrpc.DisableExchangeRequest") proto.RegisterType((*GetExchangeInfoResponse)(nil), "gctrpc.GetExchangeInfoResponse") proto.RegisterType((*GetTickerRequest)(nil), "gctrpc.GetTickerRequest") @@ -3770,224 +3812,227 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 3460 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x3a, 0x4d, 0x6f, 0xdc, 0xd6, - 0xb5, 0xe0, 0xe8, 0x73, 0xce, 0x8c, 0x34, 0xa3, 0x3b, 0xfa, 0x18, 0x8d, 0x24, 0x5b, 0x66, 0x9e, - 0x1d, 0xdb, 0x49, 0xac, 0xc4, 0x31, 0xde, 0xcb, 0x4b, 0xf2, 0xf2, 0x9e, 0x22, 0x7f, 0xc4, 0xc8, - 0x4b, 0x6c, 0xd0, 0x8e, 0x03, 0x24, 0x45, 0x09, 0x8a, 0xbc, 0x23, 0x11, 0xe2, 0x90, 0x0c, 0xc9, - 0x91, 0xac, 0xa0, 0x40, 0x81, 0x00, 0xdd, 0xb6, 0x8b, 0xa2, 0x40, 0x17, 0xfd, 0x05, 0x45, 0xbb, - 0xe9, 0x0f, 0x08, 0xba, 0x2d, 0xba, 0xec, 0xa6, 0x3f, 0xa0, 0xe8, 0xae, 0x2d, 0xba, 0xe8, 0xa6, - 0xab, 0xe2, 0x9e, 0xfb, 0x41, 0x5e, 0x72, 0x66, 0x34, 0x6e, 0xda, 0x6c, 0x24, 0xf2, 0xdc, 0x73, - 0xcf, 0xf7, 0x3d, 0xf7, 0x9c, 0xc3, 0x81, 0x7a, 0x12, 0xbb, 0xb7, 0xe2, 0x24, 0xca, 0x22, 0x32, - 0x7f, 0xe4, 0x66, 0x49, 0xec, 0xf6, 0xb6, 0x8f, 0xa2, 0xe8, 0x28, 0xa0, 0x7b, 0x4e, 0xec, 0xef, - 0x39, 0x61, 0x18, 0x65, 0x4e, 0xe6, 0x47, 0x61, 0xca, 0xb1, 0xcc, 0x36, 0x2c, 0x3f, 0xa0, 0xd9, - 0xc3, 0xb0, 0x1f, 0x59, 0xf4, 0x8b, 0x21, 0x4d, 0x33, 0xf3, 0xaf, 0x06, 0xb4, 0x14, 0x28, 0x8d, - 0xa3, 0x30, 0xa5, 0x64, 0x1d, 0xe6, 0x87, 0x71, 0xe6, 0x0f, 0x68, 0xd7, 0xd8, 0x35, 0xae, 0xd7, - 0x2d, 0xf1, 0x46, 0xf6, 0xa0, 0xe3, 0x9c, 0x3a, 0x7e, 0xe0, 0x1c, 0x06, 0xd4, 0xa6, 0xcf, 0xdd, - 0x63, 0x27, 0x3c, 0xa2, 0x69, 0xb7, 0xb6, 0x6b, 0x5c, 0x9f, 0xb1, 0x88, 0x5a, 0xba, 0x27, 0x57, - 0xc8, 0x2b, 0xb0, 0x42, 0x43, 0x06, 0xf2, 0x0a, 0xe8, 0x33, 0x88, 0xde, 0x16, 0x0b, 0x39, 0xf2, - 0x1d, 0x58, 0xf7, 0x68, 0xdf, 0x19, 0x06, 0x99, 0xdd, 0x8f, 0x12, 0xfa, 0xdc, 0x8e, 0x93, 0xe8, - 0xd4, 0xf7, 0x68, 0xd2, 0x9d, 0x45, 0x29, 0x56, 0xc5, 0xea, 0x7d, 0xb6, 0xf8, 0x58, 0xac, 0x91, - 0xdb, 0xb0, 0xa6, 0x76, 0xf9, 0x4e, 0x66, 0xbb, 0xc3, 0x24, 0xa1, 0xa1, 0x7b, 0xde, 0x9d, 0xc3, - 0x4d, 0x1d, 0xb9, 0xc9, 0x77, 0xb2, 0x03, 0xb1, 0x64, 0xbe, 0x05, 0xbd, 0x07, 0x34, 0xa4, 0x89, - 0xef, 0x4a, 0xee, 0x1f, 0x3b, 0x03, 0x2a, 0x2c, 0x42, 0x7a, 0xb0, 0x28, 0x85, 0x15, 0xfa, 0xab, - 0x77, 0x73, 0x07, 0xb6, 0x46, 0xee, 0xe4, 0x86, 0x33, 0xf7, 0xa0, 0xf3, 0x80, 0x66, 0x4a, 0x25, - 0x49, 0xb1, 0x0b, 0x0b, 0x42, 0x5b, 0x24, 0xb8, 0x68, 0xc9, 0x57, 0xf3, 0x0e, 0xac, 0xea, 0x1b, - 0x84, 0x07, 0xb6, 0xa1, 0x9e, 0x1b, 0x8c, 0x0b, 0x91, 0x03, 0xcc, 0x3b, 0xb0, 0x7e, 0xd7, 0x4f, - 0x8b, 0xa6, 0x9e, 0x46, 0xf6, 0xaf, 0x67, 0x60, 0xa3, 0xc0, 0x4c, 0xf3, 0x38, 0x81, 0xd9, 0xd0, - 0x51, 0xfe, 0xc6, 0xe7, 0xa2, 0xd4, 0x35, 0x4d, 0x6a, 0xb6, 0x72, 0x4a, 0x93, 0xc3, 0x28, 0xa5, - 0xe8, 0xcc, 0x45, 0x4b, 0xbe, 0x92, 0x97, 0x60, 0x69, 0x98, 0xfa, 0xe1, 0x91, 0x9d, 0x3a, 0xa1, - 0x77, 0x18, 0x3d, 0x47, 0xd7, 0x2d, 0x5a, 0x4d, 0x04, 0x3e, 0xe1, 0x30, 0x72, 0x05, 0x9a, 0xc7, - 0x59, 0x16, 0xdb, 0x2c, 0xa6, 0xa2, 0x61, 0x26, 0x3c, 0xd5, 0x60, 0xb0, 0xa7, 0x1c, 0x44, 0xae, - 0xc2, 0x32, 0xa2, 0x0c, 0x53, 0x9a, 0x38, 0x47, 0x34, 0xcc, 0xba, 0xf3, 0x88, 0xb4, 0xc4, 0xa0, - 0x9f, 0x48, 0x20, 0xd9, 0x01, 0x40, 0xb4, 0x38, 0x89, 0x9e, 0x9f, 0x77, 0x17, 0xb8, 0x9d, 0x18, - 0xe4, 0x31, 0x03, 0x90, 0x97, 0xa1, 0x75, 0xe8, 0xa4, 0x54, 0xc6, 0x84, 0x4f, 0xd3, 0xee, 0x22, - 0xe2, 0x2c, 0x33, 0xf0, 0x81, 0x82, 0x92, 0x1b, 0xd0, 0x4e, 0x87, 0x71, 0x1c, 0x25, 0x19, 0xf5, - 0x6c, 0x27, 0x4d, 0x69, 0x96, 0x76, 0xeb, 0x88, 0xd9, 0x52, 0xf0, 0x7d, 0x04, 0x33, 0x0d, 0x65, - 0x48, 0xc7, 0x8e, 0x9f, 0xa4, 0x5d, 0x40, 0xbc, 0xa6, 0x00, 0x3e, 0x66, 0x30, 0xc6, 0x38, 0x3f, - 0x28, 0x1c, 0xad, 0xc1, 0x19, 0x2b, 0x30, 0x47, 0x7c, 0x05, 0x56, 0x9c, 0x61, 0x76, 0x4c, 0xc3, - 0xcc, 0x77, 0x1d, 0x64, 0x1e, 0xfb, 0xdd, 0x26, 0xda, 0xac, 0xad, 0x2d, 0xec, 0xc7, 0xbe, 0x79, - 0x06, 0xed, 0x07, 0x34, 0x7b, 0xea, 0xbb, 0x27, 0x34, 0x99, 0xc2, 0xe1, 0xe4, 0x3a, 0xcc, 0x32, - 0xde, 0xe8, 0xbd, 0xc6, 0xed, 0xd5, 0x5b, 0x3c, 0x43, 0xdc, 0x92, 0xc7, 0x80, 0x49, 0x60, 0x21, - 0x06, 0xb3, 0x23, 0x6a, 0x6d, 0x67, 0xe7, 0x31, 0xf7, 0x69, 0xdd, 0xaa, 0x23, 0xe4, 0xe9, 0x79, - 0x4c, 0xcd, 0x67, 0xd0, 0x2c, 0x6e, 0x62, 0xd1, 0xe9, 0xd1, 0xc0, 0x1f, 0xf8, 0x19, 0x4d, 0x64, - 0x74, 0x2a, 0x00, 0x8b, 0x25, 0x66, 0x5e, 0x64, 0x5b, 0xb7, 0xf0, 0x99, 0xac, 0xc2, 0xdc, 0x17, - 0xc3, 0x28, 0x93, 0xb4, 0xf9, 0x8b, 0xf9, 0x93, 0x1a, 0x2c, 0x4b, 0x75, 0x44, 0x20, 0x4a, 0x99, - 0x8d, 0x0b, 0x65, 0xbe, 0x02, 0xcd, 0xc0, 0x49, 0x33, 0x7b, 0x18, 0x7b, 0xcc, 0x40, 0x22, 0x0b, - 0x35, 0x18, 0xec, 0x13, 0x0e, 0x62, 0xbe, 0x92, 0xe9, 0x00, 0xbd, 0x20, 0xb8, 0x37, 0xdd, 0xa2, - 0x32, 0x04, 0x66, 0xd9, 0x1e, 0x8c, 0x54, 0xc3, 0xc2, 0x67, 0x06, 0x3b, 0xf6, 0x8f, 0x8e, 0x31, - 0x32, 0x0d, 0x0b, 0x9f, 0x49, 0x1b, 0x66, 0x82, 0xe8, 0x0c, 0xe3, 0xd0, 0xb0, 0xd8, 0x23, 0x83, - 0x1c, 0xfa, 0x1e, 0x86, 0x9d, 0x61, 0xb1, 0x47, 0x06, 0x71, 0xd2, 0x13, 0x0c, 0x32, 0xc3, 0x62, - 0x8f, 0x2c, 0x95, 0x9e, 0x46, 0xc1, 0x70, 0x40, 0x31, 0x9e, 0x0c, 0x4b, 0xbc, 0x91, 0x2d, 0xa8, - 0xc7, 0x89, 0xef, 0x52, 0xdb, 0xc9, 0x8e, 0x31, 0x84, 0x0c, 0x6b, 0x11, 0x01, 0xfb, 0xd9, 0xb1, - 0xd9, 0x81, 0x15, 0xe5, 0x68, 0x99, 0x44, 0xcc, 0x4f, 0x61, 0x41, 0x40, 0x26, 0x3a, 0xfd, 0x75, - 0x58, 0xc8, 0x38, 0x5a, 0xb7, 0xb6, 0x3b, 0x73, 0xbd, 0x71, 0x7b, 0x5d, 0xda, 0x50, 0xb7, 0xb4, - 0x25, 0xd1, 0xcc, 0xff, 0x05, 0x52, 0xe4, 0x26, 0x1c, 0x71, 0x23, 0xa7, 0x63, 0x20, 0x9d, 0x96, - 0x4e, 0x27, 0xcd, 0x09, 0x7c, 0x89, 0x59, 0xef, 0x51, 0xe2, 0xb1, 0x24, 0x10, 0x9d, 0x7c, 0xab, - 0xa1, 0xf9, 0x11, 0x2c, 0x29, 0xc6, 0x0f, 0x33, 0x3a, 0x60, 0x06, 0x77, 0x06, 0xd1, 0x30, 0xcc, - 0x90, 0xa7, 0x61, 0x89, 0x37, 0x16, 0x81, 0x68, 0x5f, 0x64, 0x69, 0x58, 0xfc, 0x85, 0x2c, 0x43, - 0xcd, 0xf7, 0xc4, 0x8d, 0x54, 0xf3, 0x3d, 0xf3, 0xef, 0x06, 0xac, 0x14, 0x14, 0x79, 0xe1, 0xa0, - 0xac, 0x44, 0x5c, 0x6d, 0x44, 0xc4, 0xdd, 0x80, 0xd9, 0x43, 0xdf, 0x63, 0x17, 0x21, 0xb3, 0xeb, - 0x9a, 0x24, 0xa7, 0xe9, 0x61, 0x21, 0x0a, 0x43, 0x75, 0xd2, 0x93, 0xb4, 0x3b, 0x3b, 0x11, 0x95, - 0xa1, 0x54, 0xce, 0xc3, 0x5c, 0xf5, 0x3c, 0xe8, 0xb6, 0x9c, 0x2f, 0xdb, 0x72, 0x1d, 0x2f, 0x23, - 0x45, 0x5b, 0x45, 0x9e, 0x0b, 0x90, 0x03, 0x27, 0xba, 0xf5, 0xbf, 0x01, 0x22, 0x85, 0x29, 0xe2, - 0x6f, 0xb3, 0x22, 0xb4, 0x0a, 0xc1, 0x02, 0xb2, 0xf9, 0x21, 0xac, 0x95, 0x98, 0x0b, 0xe3, 0xdf, - 0xd6, 0x68, 0xf2, 0x58, 0x24, 0x15, 0x9a, 0xa9, 0x46, 0xec, 0x4d, 0x24, 0xb6, 0xef, 0xba, 0xcc, - 0xf5, 0x85, 0x6a, 0x67, 0xe2, 0xfd, 0xf8, 0x0c, 0x16, 0xc4, 0x0e, 0x11, 0x16, 0x1c, 0xa1, 0xe6, - 0x7b, 0xe4, 0x1d, 0x80, 0xc2, 0x1d, 0xc2, 0xf5, 0xda, 0x92, 0x32, 0x88, 0x4d, 0x32, 0x1a, 0x90, - 0x5d, 0x01, 0xdd, 0xec, 0x43, 0x67, 0x04, 0x0a, 0x13, 0x45, 0xd5, 0x2a, 0x42, 0x14, 0xf9, 0x4e, - 0x2e, 0x43, 0x23, 0x8b, 0x32, 0x27, 0xb0, 0x4f, 0x9d, 0x60, 0x28, 0x43, 0x16, 0x10, 0xf4, 0x8c, - 0x41, 0x30, 0x41, 0x45, 0x01, 0x8f, 0x5c, 0x96, 0xa0, 0xa2, 0xc0, 0x33, 0x1d, 0x58, 0x2f, 0x2b, - 0x2d, 0x4c, 0x38, 0xc9, 0x65, 0xaf, 0xc0, 0xa2, 0xc3, 0xb7, 0x48, 0xc5, 0x5a, 0x25, 0xc5, 0x2c, - 0x85, 0x60, 0x12, 0xbc, 0x81, 0x0e, 0xa2, 0xb0, 0xef, 0x1f, 0xc9, 0xe8, 0x78, 0x19, 0x93, 0x95, - 0x84, 0xe5, 0xf5, 0x84, 0xe7, 0x64, 0x0e, 0x72, 0x6b, 0x5a, 0xf8, 0x6c, 0xfe, 0xc0, 0x80, 0xf6, - 0xe3, 0x28, 0xc9, 0xfa, 0x51, 0xe0, 0x47, 0xfb, 0x9e, 0x97, 0xd0, 0x34, 0x65, 0xa5, 0x84, 0xc3, - 0x1f, 0x85, 0x64, 0xf2, 0x95, 0x65, 0x48, 0x37, 0xf2, 0x43, 0x1e, 0xab, 0x35, 0x61, 0xa0, 0xc8, - 0x0f, 0x59, 0xa8, 0x92, 0x5d, 0x68, 0x78, 0x34, 0x75, 0x13, 0x3f, 0x66, 0xd5, 0xad, 0x48, 0x0b, - 0x45, 0x10, 0x23, 0x7c, 0xe8, 0x04, 0x4e, 0xe8, 0x52, 0x91, 0xd9, 0xe5, 0xab, 0xb9, 0x86, 0xe9, - 0x4a, 0x49, 0x22, 0xf5, 0xf8, 0x18, 0xa3, 0xbf, 0x00, 0x16, 0xaa, 0xfc, 0x27, 0xd4, 0x63, 0x09, - 0x14, 0xe1, 0xd7, 0x95, 0x16, 0x2a, 0xab, 0x63, 0xe5, 0xa8, 0xe6, 0x36, 0x2b, 0x32, 0x73, 0x7a, - 0x4f, 0x86, 0x83, 0x81, 0x93, 0x9c, 0x4b, 0x6e, 0x21, 0xcc, 0x1e, 0x44, 0x7e, 0xc8, 0x0c, 0xc5, - 0x94, 0x92, 0x85, 0x17, 0x7b, 0x2e, 0x8a, 0x5e, 0xd3, 0x44, 0x2f, 0x5a, 0x6b, 0x46, 0xb7, 0xd6, - 0x25, 0x80, 0x98, 0x26, 0x2e, 0x0d, 0x33, 0xe7, 0x48, 0x6a, 0x5c, 0x80, 0x98, 0xc7, 0x40, 0x1e, - 0xf5, 0xfb, 0x81, 0x1f, 0x52, 0xc6, 0x56, 0x08, 0x33, 0xc1, 0xfa, 0xe3, 0x65, 0xd0, 0x39, 0xcd, - 0x54, 0x38, 0x7d, 0x04, 0x2b, 0x8f, 0xc2, 0x11, 0x8c, 0x24, 0x39, 0x63, 0x12, 0xb9, 0x5a, 0x85, - 0xdc, 0x07, 0xd0, 0x2c, 0x08, 0x9e, 0x92, 0xb7, 0xa0, 0x2e, 0x64, 0xa4, 0x32, 0x1b, 0xf4, 0x54, - 0x36, 0xa8, 0x68, 0x68, 0xe5, 0xc8, 0xe6, 0x4f, 0x0d, 0x68, 0xe4, 0x92, 0xb1, 0x7e, 0x63, 0x8e, - 0x99, 0x5b, 0x52, 0xb9, 0xa4, 0xa8, 0xe4, 0x38, 0xb7, 0xf0, 0xef, 0xbd, 0x30, 0x4b, 0xce, 0x2d, - 0x8e, 0xdc, 0x7b, 0x02, 0x90, 0x03, 0xd9, 0x85, 0x7f, 0x42, 0xe5, 0xf9, 0x65, 0x8f, 0x64, 0x0f, - 0xe6, 0xf2, 0x43, 0x5b, 0xcc, 0x7e, 0x65, 0x9b, 0x58, 0x1c, 0xef, 0xed, 0xda, 0x5b, 0x86, 0xf9, - 0xdb, 0x59, 0xd6, 0x57, 0x8c, 0x08, 0x16, 0x11, 0x83, 0xaf, 0x41, 0x83, 0x9f, 0x05, 0x96, 0x01, - 0xa4, 0xc0, 0x4d, 0x75, 0x0f, 0x45, 0x7e, 0x68, 0x01, 0x9e, 0x0d, 0x5c, 0x27, 0x6f, 0xc0, 0x12, - 0x0a, 0x6b, 0x47, 0xdc, 0x20, 0xe2, 0x60, 0xeb, 0x1b, 0x9a, 0x88, 0x22, 0x4c, 0x46, 0x62, 0x58, - 0xd3, 0xb6, 0xd8, 0x29, 0x17, 0x41, 0x5c, 0x52, 0xef, 0xca, 0xad, 0x13, 0xa4, 0xe4, 0xc6, 0x12, - 0x04, 0xc5, 0x1a, 0x37, 0x5d, 0xc7, 0xad, 0xae, 0x90, 0x3d, 0x68, 0x0a, 0x8e, 0x68, 0x19, 0x71, - 0xc5, 0xe9, 0x32, 0x36, 0xf8, 0x46, 0x44, 0x20, 0x03, 0x58, 0x2d, 0x6e, 0x50, 0x12, 0xce, 0xe1, - 0xc6, 0x77, 0xa6, 0x97, 0x30, 0xac, 0x08, 0x48, 0xdc, 0xca, 0x42, 0xef, 0x3b, 0xd0, 0x1d, 0xa7, - 0xd0, 0x08, 0xb7, 0xdf, 0xd4, 0xdd, 0xbe, 0x3a, 0x22, 0x24, 0xd3, 0x82, 0xc7, 0x7b, 0x9f, 0xc1, - 0xc6, 0x18, 0x61, 0x46, 0x10, 0xbf, 0xa1, 0x13, 0xef, 0x8c, 0x88, 0xd4, 0x62, 0x34, 0xfd, 0xc8, - 0x80, 0xde, 0xbe, 0xe7, 0x55, 0x92, 0x53, 0xde, 0x8d, 0x7e, 0xdb, 0x29, 0x77, 0x07, 0xb6, 0x46, - 0x0a, 0x24, 0xda, 0xe6, 0xe7, 0xb0, 0x63, 0xd1, 0x41, 0x74, 0x4a, 0xbf, 0x6d, 0x91, 0xcd, 0x5d, - 0xb8, 0x34, 0x8e, 0xb3, 0x90, 0xad, 0x07, 0xdd, 0x07, 0x54, 0x9f, 0x39, 0xa8, 0xc2, 0xe8, 0x4f, - 0x06, 0x2c, 0xe9, 0xd3, 0x88, 0x7f, 0x55, 0x1f, 0xfd, 0x2a, 0x90, 0x84, 0xa6, 0x99, 0x9d, 0x44, - 0x41, 0xc0, 0xda, 0x69, 0x8f, 0x06, 0xce, 0xb9, 0x98, 0x83, 0xb4, 0xd9, 0x8a, 0xc5, 0x17, 0xee, - 0x32, 0x38, 0xd9, 0x80, 0x05, 0x27, 0xf6, 0x6d, 0x16, 0x35, 0xbc, 0x97, 0x9e, 0x77, 0x62, 0xff, - 0x43, 0x7a, 0x4e, 0x4c, 0x58, 0x12, 0x0b, 0x76, 0x40, 0x4f, 0x69, 0x80, 0x35, 0xdf, 0x8c, 0xd5, - 0xe0, 0xcb, 0xff, 0xcf, 0x40, 0xac, 0xf7, 0x8d, 0x13, 0x9f, 0x85, 0x5f, 0x3e, 0x70, 0x59, 0x40, - 0x69, 0x5a, 0x02, 0x2e, 0xb5, 0x33, 0x3f, 0x87, 0xcd, 0x11, 0xb6, 0x10, 0x39, 0xea, 0x3d, 0x68, - 0xe9, 0x63, 0x1b, 0x99, 0xa7, 0x54, 0xd5, 0xaa, 0x6d, 0xb4, 0x96, 0xfb, 0x1a, 0x1d, 0x51, 0x7d, - 0x22, 0x8e, 0xe5, 0x64, 0x6a, 0x78, 0x62, 0x7e, 0x01, 0xab, 0x39, 0xf0, 0x20, 0x0a, 0x4f, 0x69, - 0x92, 0xb2, 0x68, 0x23, 0x30, 0xdb, 0x4f, 0xa2, 0x81, 0x34, 0x35, 0x7b, 0x66, 0x75, 0x5b, 0x16, - 0x89, 0x30, 0xa8, 0x65, 0x11, 0xc3, 0x49, 0x9c, 0x4c, 0xde, 0x52, 0xf8, 0xcc, 0xea, 0x64, 0x1f, - 0x89, 0x50, 0x1b, 0xd7, 0x78, 0xa8, 0x36, 0x04, 0x8c, 0x71, 0x31, 0x9f, 0x61, 0xf9, 0x58, 0x14, - 0x45, 0xe8, 0xf8, 0x3f, 0xd0, 0xe0, 0x3a, 0xb2, 0x9d, 0x52, 0xbf, 0x6d, 0x4d, 0xbf, 0x92, 0x98, - 0x16, 0xf4, 0x15, 0xd4, 0xfc, 0x4b, 0x0d, 0x9a, 0x58, 0xb1, 0xde, 0xa5, 0x99, 0xe3, 0x07, 0x93, - 0x6b, 0x69, 0x5e, 0x83, 0xd6, 0x54, 0x0d, 0xfa, 0x12, 0x2c, 0x15, 0x87, 0x19, 0xe7, 0xb2, 0x99, - 0x2d, 0x8c, 0x32, 0xce, 0xc9, 0x55, 0x58, 0xc6, 0xd6, 0x3a, 0xc7, 0xe2, 0x31, 0xb3, 0x84, 0x50, - 0x85, 0xa6, 0x37, 0x02, 0x73, 0xa5, 0x46, 0x80, 0x2d, 0x63, 0x31, 0x6d, 0xa7, 0xbe, 0xa7, 0xfa, - 0x04, 0x84, 0x3c, 0xf1, 0xbd, 0xc2, 0x32, 0xee, 0x5e, 0x28, 0x2c, 0xe3, 0x6e, 0xd6, 0x03, 0x25, - 0x14, 0xc7, 0x8e, 0x38, 0xe2, 0xc1, 0x76, 0x78, 0xc6, 0x6a, 0x4a, 0xe0, 0x53, 0x7f, 0x80, 0x23, - 0xc6, 0x34, 0x73, 0xb2, 0xa1, 0x9c, 0xb3, 0x88, 0xb7, 0xbc, 0x4d, 0x83, 0x62, 0x9b, 0x96, 0x37, - 0x75, 0x0d, 0xad, 0xa9, 0xbb, 0x0c, 0x8d, 0x28, 0xa6, 0xa1, 0x2d, 0x5a, 0xec, 0x26, 0xaf, 0x1e, - 0x18, 0xe8, 0x19, 0x42, 0xc4, 0xc8, 0x04, 0x6d, 0x9e, 0x4e, 0xd3, 0x97, 0xea, 0x86, 0xa9, 0x95, - 0x0d, 0x23, 0x1b, 0xc1, 0x99, 0x8b, 0x1a, 0x41, 0x73, 0x1f, 0xab, 0x62, 0xc9, 0x58, 0x84, 0xcf, - 0xab, 0x30, 0x8f, 0x66, 0x92, 0x91, 0xb3, 0xaa, 0xb5, 0x31, 0x22, 0x28, 0x2c, 0x81, 0x63, 0x7e, - 0x80, 0x83, 0x59, 0x5c, 0x9a, 0x46, 0xf4, 0x4d, 0x58, 0xe4, 0x5e, 0x51, 0x51, 0xb3, 0x80, 0xef, - 0x0f, 0x3d, 0xf3, 0xf7, 0x06, 0x90, 0x27, 0xc3, 0xc3, 0x81, 0x3f, 0x3d, 0xb5, 0xe9, 0x1b, 0x74, - 0x02, 0xb3, 0x18, 0x26, 0x3c, 0x1c, 0xf1, 0xb9, 0x14, 0x21, 0xb3, 0xe5, 0x08, 0xc9, 0xdd, 0x39, - 0x37, 0xba, 0x47, 0x9f, 0x2f, 0x3a, 0x9f, 0xa5, 0xf8, 0xc0, 0xa7, 0x61, 0x66, 0x8b, 0x61, 0x0b, - 0x4b, 0xf1, 0x08, 0x78, 0xe8, 0x99, 0x4f, 0xa0, 0xa3, 0x69, 0x26, 0x2c, 0x7d, 0x05, 0x9a, 0x5c, - 0x80, 0x38, 0x70, 0x5c, 0x35, 0x76, 0x6d, 0x20, 0xec, 0x31, 0x82, 0x26, 0xd9, 0xeb, 0xcf, 0x06, - 0x90, 0x03, 0x76, 0x71, 0x05, 0x53, 0xdb, 0x8b, 0x05, 0x0e, 0xef, 0x92, 0x72, 0x7a, 0x75, 0x01, - 0x79, 0xa8, 0x33, 0x9b, 0xd1, 0x98, 0x29, 0x4b, 0xcf, 0xbe, 0xe0, 0x28, 0xa4, 0x72, 0x6a, 0xaf, - 0xc2, 0xf2, 0x99, 0x13, 0x04, 0x34, 0xb3, 0xe5, 0x5d, 0x29, 0x66, 0xa6, 0x1c, 0x2a, 0x3b, 0x2e, - 0xe9, 0xaf, 0x85, 0xdc, 0x5f, 0xac, 0x25, 0xd2, 0xf4, 0x15, 0x77, 0xdf, 0x1d, 0x58, 0xe7, 0xe0, - 0xfd, 0x20, 0x98, 0xfa, 0x0c, 0x99, 0x3f, 0xab, 0xc1, 0x46, 0x65, 0x9b, 0xba, 0x24, 0xf4, 0x13, - 0x70, 0x4d, 0xa9, 0x3b, 0x7a, 0xc3, 0x2d, 0xf1, 0x2a, 0x76, 0xf5, 0x7e, 0x6d, 0xc0, 0x3c, 0x07, - 0x4d, 0xf4, 0xc6, 0x67, 0xd2, 0xfd, 0x22, 0xc7, 0xf0, 0xfa, 0xf7, 0xbf, 0xa6, 0x63, 0xc6, 0xff, - 0x3d, 0xc1, 0x9d, 0xbc, 0x3c, 0xe4, 0x71, 0xc3, 0x21, 0xbd, 0xf7, 0xa0, 0x5d, 0x46, 0x18, 0x51, - 0xb2, 0xad, 0x16, 0x4b, 0xb6, 0x7a, 0xb1, 0x3a, 0xe3, 0x3d, 0xf4, 0xbd, 0x53, 0x1a, 0x66, 0xea, - 0x8e, 0xfb, 0xda, 0x80, 0xd6, 0x41, 0x14, 0x7a, 0x3e, 0xcb, 0x8f, 0x8f, 0x9d, 0xc4, 0x19, 0xa4, - 0x64, 0x9b, 0x55, 0x36, 0x02, 0x24, 0x87, 0xac, 0x0a, 0x30, 0x66, 0x9c, 0xb5, 0x03, 0xe0, 0x1e, - 0x53, 0xf7, 0xc4, 0x16, 0xf3, 0x25, 0x16, 0xf4, 0x75, 0x84, 0xbc, 0xef, 0x7b, 0x29, 0x79, 0x0d, - 0x3a, 0xf9, 0xb2, 0xed, 0x84, 0x9e, 0x2d, 0x86, 0x4b, 0x38, 0x6f, 0x56, 0x78, 0xfb, 0xa1, 0xb7, - 0x9f, 0x9e, 0xe0, 0x54, 0x5c, 0xcd, 0x54, 0x6c, 0xed, 0xc0, 0xb6, 0x14, 0x7c, 0x1f, 0xc1, 0xe6, - 0xdf, 0x0c, 0xcc, 0x77, 0x52, 0x2b, 0xe1, 0xed, 0x7c, 0x8c, 0x82, 0xd3, 0x35, 0xcd, 0x65, 0xb5, - 0x92, 0xcb, 0x08, 0xcc, 0xfa, 0x19, 0x1d, 0xc8, 0x34, 0xc2, 0x9e, 0xc9, 0xfb, 0xd0, 0x56, 0x1a, - 0xdb, 0x31, 0x9a, 0x45, 0x1c, 0x93, 0x8d, 0xbc, 0x4d, 0xd0, 0xac, 0x66, 0xb5, 0xdc, 0x92, 0x19, - 0xe5, 0xf1, 0x9a, 0xbb, 0xf0, 0x78, 0xb1, 0xac, 0xe4, 0xa2, 0xb5, 0xe7, 0x45, 0x11, 0x85, 0x6f, - 0x5c, 0x6a, 0xea, 0x0e, 0x33, 0xea, 0x89, 0xc2, 0x48, 0xbd, 0x9b, 0x7f, 0x34, 0xa0, 0xb5, 0xef, - 0x79, 0xa8, 0xf7, 0x34, 0x69, 0x42, 0x6a, 0x59, 0xbb, 0x40, 0xcb, 0x99, 0x7f, 0x52, 0xcb, 0x6f, - 0x9c, 0x44, 0xc6, 0x18, 0xc1, 0x34, 0xa1, 0x9d, 0xeb, 0x39, 0xda, 0xbd, 0xe6, 0x7f, 0x00, 0xe1, - 0xc5, 0xb4, 0x66, 0x8e, 0x32, 0xd6, 0x1a, 0x74, 0x34, 0x2c, 0x91, 0x6b, 0xee, 0xc3, 0xf5, 0x07, - 0x34, 0x3b, 0x48, 0xce, 0xe3, 0x2c, 0x92, 0xc5, 0xcb, 0x5d, 0x1a, 0x47, 0xa9, 0x2f, 0x33, 0x17, - 0x9d, 0x2a, 0xfb, 0xfc, 0xc6, 0x80, 0x1b, 0x53, 0x10, 0x12, 0x2a, 0x7c, 0xb7, 0x3a, 0x4d, 0xf8, - 0xbf, 0x42, 0x23, 0x39, 0x1d, 0x95, 0x5b, 0x0a, 0xc2, 0xd3, 0x45, 0x4e, 0xb2, 0xf7, 0x2e, 0x2c, - 0xeb, 0x8b, 0x2f, 0x94, 0x2a, 0x02, 0xb8, 0x76, 0x81, 0x10, 0xd3, 0xc4, 0xdc, 0x35, 0x58, 0x76, - 0x35, 0x12, 0x82, 0x51, 0x09, 0x6a, 0x1e, 0xc0, 0xcb, 0x17, 0x72, 0x13, 0x66, 0x1b, 0xdb, 0x8f, - 0x99, 0xbf, 0x9c, 0x85, 0x8d, 0x4f, 0xfd, 0xec, 0xd8, 0x4b, 0x9c, 0x33, 0x19, 0x7d, 0xd3, 0x08, - 0x59, 0x6a, 0xd5, 0x6a, 0xd5, 0xee, 0xf2, 0x26, 0xac, 0x44, 0x21, 0xc5, 0x8a, 0xd2, 0x8e, 0x9d, - 0x34, 0x3d, 0x8b, 0x12, 0x79, 0x97, 0xb6, 0xa2, 0x90, 0xb2, 0xaa, 0xf2, 0xb1, 0x00, 0x97, 0x6e, - 0xe3, 0xd9, 0xf2, 0x6d, 0xdc, 0x86, 0x99, 0xd8, 0x0f, 0xc5, 0x84, 0x9c, 0x3d, 0xb2, 0xbb, 0x33, - 0x4b, 0x1c, 0xaf, 0x40, 0x59, 0xdc, 0x9d, 0x08, 0x55, 0x74, 0x8b, 0x33, 0xdb, 0x85, 0xd2, 0xcc, - 0xb6, 0x60, 0x93, 0x45, 0xbd, 0x47, 0xbd, 0x0c, 0x0d, 0xf1, 0x68, 0x67, 0xce, 0x91, 0x28, 0x78, - 0x41, 0x80, 0x9e, 0x3a, 0x47, 0x85, 0x7a, 0x08, 0xb4, 0x7a, 0x68, 0x07, 0xa0, 0x4f, 0xa9, 0xad, - 0x95, 0xbe, 0xf5, 0x3e, 0xa5, 0x3c, 0xe9, 0xb2, 0xc2, 0xe8, 0xd0, 0x09, 0x4f, 0x6c, 0xec, 0x38, - 0x9b, 0x5c, 0x1c, 0x06, 0xf8, 0x98, 0x75, 0x9d, 0x57, 0xa0, 0x89, 0x8b, 0x52, 0xa6, 0x25, 0x6e, - 0x51, 0x06, 0xdb, 0xcf, 0x7b, 0x67, 0x44, 0x71, 0xfd, 0xec, 0xbc, 0xbb, 0x9c, 0xef, 0x3f, 0xf0, - 0xb3, 0x73, 0xb5, 0x1f, 0x6d, 0x96, 0x9c, 0x77, 0x5b, 0xf9, 0xfe, 0x03, 0x0e, 0x62, 0xe2, 0xa5, - 0x67, 0x7e, 0x9f, 0xda, 0x6e, 0xe4, 0xd1, 0x6e, 0x9b, 0x5b, 0x19, 0x21, 0x07, 0x91, 0x87, 0x7d, - 0xc0, 0x99, 0x9f, 0x14, 0x5a, 0x91, 0x15, 0xde, 0xb0, 0x30, 0xa0, 0xfa, 0x14, 0x7f, 0x13, 0xda, - 0x32, 0x5c, 0x8a, 0x3f, 0x3f, 0x48, 0x68, 0x3a, 0x0c, 0x32, 0xf9, 0xf3, 0x03, 0xfe, 0x76, 0xfb, - 0x17, 0x9b, 0xb0, 0xfc, 0x20, 0xe2, 0x01, 0xfa, 0x94, 0xf9, 0x25, 0x21, 0x8f, 0x60, 0x41, 0xfc, - 0x78, 0x81, 0xac, 0x17, 0xce, 0x6d, 0x61, 0xe4, 0xdf, 0xdb, 0xa8, 0xc0, 0x45, 0xc6, 0xe9, 0x7c, - 0xf5, 0xbb, 0x3f, 0xfc, 0xb8, 0xb6, 0x44, 0x1a, 0x7b, 0xa7, 0x6f, 0xec, 0x1d, 0xd1, 0xcc, 0x67, - 0x54, 0x5c, 0x68, 0x16, 0x3f, 0xc8, 0x93, 0xad, 0xc2, 0xee, 0xf2, 0x77, 0xfd, 0xde, 0xf6, 0xe8, - 0x45, 0x41, 0xbf, 0x8b, 0xf4, 0x09, 0x69, 0x0b, 0xfa, 0xea, 0xfb, 0x3d, 0xf9, 0x12, 0x5a, 0xa5, - 0xef, 0xf7, 0xc4, 0xcc, 0x49, 0x8d, 0xfb, 0x61, 0x42, 0xef, 0xa5, 0x89, 0x38, 0x82, 0xeb, 0x25, - 0xe4, 0xda, 0x35, 0x3b, 0x8c, 0xab, 0xc7, 0xb9, 0x48, 0xce, 0x6f, 0x1b, 0x37, 0x49, 0x8a, 0x5d, - 0x45, 0xf1, 0x47, 0x00, 0x53, 0xf1, 0xbe, 0x3c, 0x42, 0x55, 0xcd, 0x9a, 0x5b, 0xc8, 0x77, 0x8d, - 0x74, 0x4a, 0xda, 0xa2, 0x55, 0x9f, 0xc3, 0xf2, 0xbd, 0xf0, 0xdf, 0xa3, 0xef, 0x0e, 0xf2, 0xdd, - 0x30, 0x09, 0xe3, 0xcb, 0x07, 0x2b, 0x45, 0x75, 0x3f, 0x85, 0xba, 0xfa, 0xb8, 0x49, 0xba, 0x05, - 0x25, 0xb4, 0xcf, 0xe8, 0xbd, 0x31, 0x1f, 0x49, 0xa5, 0x0f, 0xcd, 0x25, 0xa1, 0x15, 0xff, 0xe4, - 0xc9, 0x08, 0x7f, 0x0e, 0x90, 0x7f, 0x35, 0x25, 0x9b, 0x15, 0xca, 0x2a, 0x48, 0x7a, 0xa3, 0x96, - 0x04, 0xf9, 0x75, 0x24, 0xdf, 0x26, 0xcb, 0x1a, 0xf9, 0x54, 0x44, 0xa1, 0xfa, 0xb8, 0xa5, 0x45, - 0x61, 0xf9, 0x3b, 0x6b, 0x6f, 0xfc, 0x07, 0x36, 0xe9, 0x14, 0x53, 0x86, 0xa0, 0x2a, 0xdb, 0x98, - 0x06, 0x47, 0xb0, 0xa4, 0x7d, 0x71, 0x23, 0xdb, 0xa3, 0xb8, 0x28, 0x3d, 0x76, 0xc6, 0xac, 0x0a, - 0x56, 0x9b, 0xc8, 0xaa, 0x43, 0x56, 0xca, 0xac, 0x52, 0x72, 0x82, 0x3f, 0x3a, 0x2a, 0x7c, 0x98, - 0x22, 0x45, 0x5a, 0xd5, 0xaf, 0x74, 0xbd, 0x4b, 0xe3, 0x96, 0xe5, 0x4c, 0x0e, 0x79, 0xad, 0x12, - 0x22, 0x78, 0x89, 0xcc, 0x8e, 0xa1, 0xc6, 0x1d, 0xce, 0x3f, 0x47, 0x69, 0x0e, 0xd7, 0xbe, 0x5a, - 0xf5, 0x36, 0x47, 0xac, 0x08, 0xea, 0x6b, 0x48, 0xbd, 0x45, 0xa4, 0xcf, 0x5d, 0x4e, 0x8b, 0xfb, - 0x44, 0xcd, 0x09, 0x35, 0x9f, 0x94, 0x3f, 0x26, 0x69, 0x99, 0xa1, 0xf2, 0x49, 0xa9, 0x92, 0x19, - 0xd4, 0x47, 0x23, 0xf2, 0x7d, 0xfd, 0xdb, 0x94, 0x9c, 0x95, 0x9b, 0x13, 0x87, 0xdb, 0x95, 0xd3, - 0x32, 0x76, 0x00, 0x6e, 0x5e, 0x46, 0xce, 0x9b, 0x64, 0xa3, 0xcc, 0x59, 0x0c, 0xd3, 0xc9, 0x57, - 0x06, 0x74, 0x46, 0x8c, 0x6a, 0x73, 0x09, 0xc6, 0x0f, 0x96, 0x73, 0x09, 0x26, 0xcd, 0x7a, 0x4d, - 0x94, 0x60, 0xdb, 0x44, 0x09, 0x1c, 0xcf, 0x53, 0x12, 0x88, 0x8b, 0x8a, 0x45, 0xe6, 0x0f, 0x0d, - 0x58, 0x1f, 0x3d, 0x96, 0x25, 0x57, 0x25, 0x8f, 0x89, 0x03, 0xe3, 0xde, 0xb5, 0x8b, 0xd0, 0x84, - 0x34, 0x57, 0x51, 0x9a, 0xcb, 0x66, 0x8f, 0x49, 0x93, 0x20, 0xee, 0x28, 0x81, 0xce, 0xb0, 0xbb, - 0xd1, 0x07, 0x9f, 0x64, 0xb7, 0x60, 0xf0, 0x91, 0xf3, 0xe1, 0xde, 0x95, 0x09, 0x18, 0x7a, 0xfa, - 0x22, 0x6b, 0xc2, 0x21, 0x38, 0x2d, 0x54, 0x13, 0x54, 0x71, 0x46, 0xf3, 0xc1, 0xa2, 0x76, 0x46, - 0x2b, 0xb3, 0x52, 0xed, 0x8c, 0x56, 0xc7, 0x97, 0x95, 0x33, 0x8a, 0xcc, 0x70, 0x94, 0x49, 0x3e, - 0xc3, 0x63, 0x23, 0x5a, 0xeb, 0x6e, 0xf9, 0xa8, 0xa7, 0xa3, 0x8e, 0x8d, 0xde, 0x3c, 0x57, 0x52, - 0x25, 0xef, 0xd8, 0x99, 0xf5, 0x2c, 0x58, 0x94, 0xe8, 0x64, 0xa3, 0x4c, 0x40, 0x52, 0x1e, 0x39, - 0x0b, 0x33, 0x37, 0x90, 0xe8, 0x8a, 0xd9, 0x2c, 0x12, 0x65, 0x34, 0x0f, 0xa1, 0x51, 0x98, 0xfb, - 0x10, 0x95, 0x64, 0xab, 0x63, 0xae, 0xde, 0xd6, 0xc8, 0x35, 0x3d, 0x95, 0x98, 0x2d, 0xc6, 0x20, - 0x45, 0x84, 0x22, 0x8f, 0xc2, 0x54, 0x24, 0xe7, 0x51, 0x1d, 0x0d, 0xe5, 0x3c, 0x46, 0x8d, 0x51, - 0x34, 0x1e, 0x2e, 0x22, 0x28, 0x1e, 0x09, 0xb4, 0x4a, 0xd3, 0x08, 0x72, 0x69, 0xec, 0x98, 0xa2, - 0x74, 0x15, 0x8f, 0x19, 0x63, 0xe8, 0x25, 0x00, 0xe7, 0xe7, 0x04, 0x41, 0xee, 0x0f, 0x9e, 0x22, - 0x79, 0xaf, 0xae, 0xf9, 0x5a, 0x1b, 0x4a, 0x68, 0xbe, 0xd6, 0x1b, 0xfb, 0x4a, 0x8a, 0xa4, 0x9c, - 0xd6, 0x33, 0x58, 0x94, 0x4d, 0x62, 0xee, 0xe8, 0x52, 0x7b, 0xdc, 0xeb, 0x56, 0x17, 0x04, 0x55, - 0xcd, 0xd9, 0x8e, 0xe7, 0x21, 0x55, 0xe1, 0x88, 0x42, 0xcb, 0x98, 0x3b, 0xa2, 0xda, 0x6d, 0xe6, - 0x8e, 0x18, 0xd5, 0x63, 0x6a, 0x8e, 0xe0, 0xa7, 0x5d, 0xf1, 0xf8, 0x95, 0x01, 0x57, 0x2e, 0xec, - 0xf8, 0xc8, 0xeb, 0x2f, 0xd0, 0x1c, 0x72, 0x81, 0xde, 0x78, 0xe1, 0x76, 0xd2, 0xbc, 0x8e, 0x62, - 0x9a, 0xe6, 0x8e, 0xbc, 0x80, 0x70, 0x9b, 0xc7, 0xd1, 0x55, 0x6f, 0xc9, 0x84, 0xfe, 0xb9, 0x01, - 0x97, 0x2f, 0xa0, 0x4b, 0x6e, 0x4d, 0x29, 0x80, 0x14, 0x78, 0x6f, 0x6a, 0x7c, 0x21, 0xee, 0x35, - 0x14, 0x77, 0xd7, 0xdc, 0x9a, 0x20, 0x2e, 0x13, 0xf6, 0x7b, 0xb0, 0xa5, 0x3a, 0x43, 0x8d, 0xee, - 0xfd, 0x61, 0xe8, 0xa5, 0x44, 0x85, 0xf5, 0x98, 0xf6, 0x31, 0x0f, 0x9c, 0x72, 0xc3, 0xa0, 0xdf, - 0x29, 0x67, 0x62, 0x95, 0x8b, 0xd1, 0x67, 0xb4, 0x19, 0xf7, 0x18, 0x56, 0xe4, 0xbe, 0xfb, 0xbe, - 0x93, 0x7d, 0x63, 0x9e, 0xbb, 0xc8, 0xb3, 0x67, 0xae, 0x15, 0x79, 0xf6, 0x7d, 0x27, 0x93, 0x1c, - 0x0f, 0xe7, 0xf1, 0x27, 0xd7, 0x6f, 0xfe, 0x23, 0x00, 0x00, 0xff, 0xff, 0x49, 0x38, 0x72, 0xc9, - 0xa5, 0x2d, 0x00, 0x00, + // 3508 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x1a, 0x4d, 0x6f, 0xdc, 0xc6, + 0x15, 0x5c, 0x7d, 0xbf, 0x5d, 0x69, 0x57, 0xa3, 0xaf, 0xd5, 0x4a, 0xb2, 0xe5, 0x49, 0xed, 0xd8, + 0x4e, 0x62, 0x25, 0x8e, 0xd1, 0xa6, 0x49, 0x9a, 0x56, 0x91, 0x3f, 0x62, 0xa4, 0x89, 0x05, 0xda, + 0x71, 0x80, 0xa4, 0x28, 0x41, 0x91, 0xb3, 0x12, 0x21, 0x8a, 0x64, 0x48, 0xae, 0x64, 0x05, 0x05, + 0x0a, 0x04, 0xe8, 0xb5, 0x3d, 0x14, 0x05, 0x7a, 0xe8, 0xa5, 0xd7, 0x02, 0xbd, 0xf4, 0x07, 0x04, + 0xbd, 0x16, 0x3d, 0xf6, 0xd2, 0x1f, 0x50, 0xf4, 0xd6, 0x16, 0x3d, 0xf4, 0xd2, 0x53, 0x31, 0x6f, + 0x66, 0x48, 0x0e, 0xc9, 0x5d, 0xad, 0x9b, 0x36, 0x17, 0x89, 0x7c, 0xf3, 0xe6, 0x7d, 0xcf, 0x9b, + 0xf7, 0x1e, 0x17, 0xe6, 0xe2, 0xc8, 0xb9, 0x15, 0xc5, 0x61, 0x1a, 0x92, 0xe9, 0x43, 0x27, 0x8d, + 0x23, 0xa7, 0xb7, 0x79, 0x18, 0x86, 0x87, 0x3e, 0xdb, 0xb1, 0x23, 0x6f, 0xc7, 0x0e, 0x82, 0x30, + 0xb5, 0x53, 0x2f, 0x0c, 0x12, 0x81, 0x45, 0x3b, 0xb0, 0xf0, 0x80, 0xa5, 0x0f, 0x83, 0x7e, 0x68, + 0xb2, 0xcf, 0x06, 0x2c, 0x49, 0xe9, 0x3f, 0x0d, 0x68, 0x67, 0xa0, 0x24, 0x0a, 0x83, 0x84, 0x91, + 0x55, 0x98, 0x1e, 0x44, 0xa9, 0x77, 0xc2, 0xba, 0xc6, 0xb6, 0x71, 0x7d, 0xce, 0x94, 0x6f, 0x64, + 0x07, 0x96, 0xec, 0x53, 0xdb, 0xf3, 0xed, 0x03, 0x9f, 0x59, 0xec, 0x99, 0x73, 0x64, 0x07, 0x87, + 0x2c, 0xe9, 0x36, 0xb6, 0x8d, 0xeb, 0x13, 0x26, 0xc9, 0x96, 0xee, 0xa9, 0x15, 0xf2, 0x12, 0x2c, + 0xb2, 0x80, 0x83, 0xdc, 0x02, 0xfa, 0x04, 0xa2, 0x77, 0xe4, 0x42, 0x8e, 0x7c, 0x07, 0x56, 0x5d, + 0xd6, 0xb7, 0x07, 0x7e, 0x6a, 0xf5, 0xc3, 0x98, 0x3d, 0xb3, 0xa2, 0x38, 0x3c, 0xf5, 0x5c, 0x16, + 0x77, 0x27, 0x51, 0x8a, 0x65, 0xb9, 0x7a, 0x9f, 0x2f, 0xee, 0xcb, 0x35, 0x72, 0x1b, 0x56, 0xb2, + 0x5d, 0x9e, 0x9d, 0x5a, 0xce, 0x20, 0x8e, 0x59, 0xe0, 0x9c, 0x77, 0xa7, 0x70, 0xd3, 0x92, 0xda, + 0xe4, 0xd9, 0xe9, 0x9e, 0x5c, 0xa2, 0x6f, 0x40, 0xef, 0x01, 0x0b, 0x58, 0xec, 0x39, 0x8a, 0xfb, + 0x87, 0xf6, 0x09, 0x93, 0x16, 0x21, 0x3d, 0x98, 0x55, 0xc2, 0x4a, 0xfd, 0xb3, 0x77, 0xba, 0x05, + 0x1b, 0xb5, 0x3b, 0x85, 0xe1, 0xe8, 0x0e, 0x2c, 0x3d, 0x60, 0x69, 0xa6, 0x92, 0xa2, 0xd8, 0x85, + 0x19, 0xa9, 0x2d, 0x12, 0x9c, 0x35, 0xd5, 0x2b, 0xbd, 0x03, 0xcb, 0xfa, 0x06, 0xe9, 0x81, 0x4d, + 0x98, 0xcb, 0x0d, 0x26, 0x84, 0xc8, 0x01, 0xf4, 0x36, 0xac, 0x14, 0x76, 0x3d, 0x7a, 0xb2, 0x6f, + 0x32, 0xb1, 0x6d, 0x1d, 0x66, 0xc3, 0x34, 0xb2, 0x9c, 0xd0, 0x55, 0xa2, 0xcf, 0x84, 0x69, 0xb4, + 0x17, 0xba, 0x8c, 0xde, 0x81, 0xd5, 0xbb, 0x5e, 0x52, 0x74, 0xcf, 0x38, 0xfa, 0x7e, 0x39, 0x01, + 0x6b, 0x05, 0x56, 0x5a, 0x94, 0x10, 0x98, 0x0c, 0xec, 0x2c, 0x46, 0xf0, 0xb9, 0xa8, 0x69, 0x43, + 0xd3, 0x94, 0xaf, 0x9c, 0xb2, 0xf8, 0x20, 0x4c, 0x18, 0x06, 0xc0, 0xac, 0xa9, 0x5e, 0xc9, 0x0b, + 0x30, 0x3f, 0x48, 0xbc, 0xe0, 0xd0, 0x4a, 0xec, 0xc0, 0x3d, 0x08, 0x9f, 0xa1, 0xbb, 0x67, 0xcd, + 0x16, 0x02, 0x1f, 0x0b, 0x18, 0xb9, 0x02, 0xad, 0xa3, 0x34, 0x8d, 0x2c, 0x1e, 0x87, 0xe1, 0x20, + 0x95, 0xde, 0x6d, 0x72, 0xd8, 0x13, 0x01, 0x22, 0x57, 0x61, 0x01, 0x51, 0x06, 0x09, 0x8b, 0xed, + 0x43, 0x16, 0xa4, 0xdd, 0x69, 0x44, 0x9a, 0xe7, 0xd0, 0x8f, 0x14, 0x90, 0x6c, 0x01, 0x20, 0x5a, + 0x14, 0x87, 0xcf, 0xce, 0xbb, 0x33, 0xc2, 0xb6, 0x1c, 0xb2, 0xcf, 0x01, 0xe4, 0x45, 0x68, 0x1f, + 0xd8, 0x09, 0x53, 0x71, 0xe4, 0xb1, 0xa4, 0x3b, 0x8b, 0x38, 0x0b, 0x1c, 0xbc, 0x97, 0x41, 0xc9, + 0x0d, 0xe8, 0x24, 0x83, 0x28, 0x0a, 0xe3, 0x94, 0xb9, 0x96, 0x9d, 0x24, 0x2c, 0x4d, 0xba, 0x73, + 0x88, 0xd9, 0xce, 0xe0, 0xbb, 0x08, 0xe6, 0x1a, 0xaa, 0x63, 0x10, 0xd9, 0x5e, 0x9c, 0x74, 0x01, + 0xf1, 0x5a, 0x12, 0xb8, 0xcf, 0x61, 0x9c, 0x71, 0x7e, 0xb8, 0x04, 0x5a, 0x53, 0x30, 0xce, 0xc0, + 0x02, 0xf1, 0x25, 0x58, 0xb4, 0x07, 0xe9, 0x11, 0x0b, 0x52, 0xcf, 0xb1, 0x91, 0x79, 0xe4, 0x75, + 0x5b, 0x68, 0xb3, 0x8e, 0xb6, 0xb0, 0x1b, 0x79, 0xf4, 0x0c, 0x3a, 0x0f, 0x58, 0xfa, 0xc4, 0x73, + 0x8e, 0x59, 0x3c, 0x86, 0xc3, 0xc9, 0x75, 0x98, 0xe4, 0xbc, 0xd1, 0x7b, 0xcd, 0xdb, 0xcb, 0xb7, + 0x44, 0x56, 0xb9, 0xa5, 0x8e, 0x0e, 0x97, 0xc0, 0x44, 0x0c, 0x6e, 0x47, 0xd4, 0xda, 0x4a, 0xcf, + 0x23, 0xe1, 0xd3, 0x39, 0x73, 0x0e, 0x21, 0x4f, 0xce, 0x23, 0x46, 0x9f, 0x42, 0xab, 0xb8, 0x89, + 0x47, 0xb4, 0xcb, 0x7c, 0xef, 0xc4, 0x4b, 0x59, 0xac, 0x22, 0x3a, 0x03, 0xf0, 0x58, 0xe2, 0xe6, + 0x45, 0xb6, 0x73, 0x26, 0x3e, 0x93, 0x65, 0x98, 0xfa, 0x6c, 0x10, 0xa6, 0x8a, 0xb6, 0x78, 0xa1, + 0xbf, 0x68, 0xc0, 0x82, 0x52, 0x47, 0x06, 0xa2, 0x92, 0xd9, 0xb8, 0x50, 0xe6, 0x2b, 0xd0, 0xf2, + 0xed, 0x24, 0xb5, 0x06, 0x91, 0xcb, 0x0d, 0x24, 0x33, 0x57, 0x93, 0xc3, 0x3e, 0x12, 0x20, 0xee, + 0x2b, 0x95, 0x42, 0xd0, 0x0b, 0x92, 0x7b, 0xcb, 0x29, 0x2a, 0x43, 0x60, 0x92, 0xef, 0xc1, 0x48, + 0x35, 0x4c, 0x7c, 0xe6, 0xb0, 0x23, 0xef, 0xf0, 0x08, 0x23, 0xd3, 0x30, 0xf1, 0x99, 0x74, 0x60, + 0xc2, 0x0f, 0xcf, 0x30, 0x0e, 0x0d, 0x93, 0x3f, 0x72, 0xc8, 0x81, 0xe7, 0x62, 0xd8, 0x19, 0x26, + 0x7f, 0xe4, 0x10, 0x3b, 0x39, 0xc6, 0x20, 0x33, 0x4c, 0xfe, 0xc8, 0xd3, 0xef, 0x69, 0xe8, 0x0f, + 0x4e, 0x18, 0xc6, 0x93, 0x61, 0xca, 0x37, 0xb2, 0x01, 0x73, 0x51, 0xec, 0x39, 0xcc, 0xb2, 0xd3, + 0x23, 0x0c, 0x21, 0xc3, 0x9c, 0x45, 0xc0, 0x6e, 0x7a, 0x44, 0x97, 0x60, 0x31, 0x73, 0xb4, 0x4a, + 0x3c, 0xf4, 0x63, 0x98, 0x91, 0x90, 0x91, 0x4e, 0x7f, 0x15, 0x66, 0x52, 0x81, 0xd6, 0x6d, 0x6c, + 0x4f, 0x5c, 0x6f, 0xde, 0x5e, 0x55, 0x36, 0xd4, 0x2d, 0x6d, 0x2a, 0x34, 0xfa, 0x5d, 0x20, 0x45, + 0x6e, 0xd2, 0x11, 0x37, 0x72, 0x3a, 0x06, 0xd2, 0x69, 0xeb, 0x74, 0x92, 0x9c, 0xc0, 0xe7, 0x98, + 0x29, 0x1f, 0xc5, 0x2e, 0x4f, 0x02, 0xe1, 0xf1, 0xd7, 0x1a, 0x9a, 0x1f, 0xc0, 0x7c, 0xc6, 0xf8, + 0x61, 0xca, 0x4e, 0xb8, 0xc1, 0xed, 0x93, 0x70, 0x10, 0xa4, 0xc8, 0xd3, 0x30, 0xe5, 0x1b, 0x8f, + 0x40, 0xb4, 0x2f, 0xb2, 0x34, 0x4c, 0xf1, 0x42, 0x16, 0xa0, 0xe1, 0xb9, 0xf2, 0x16, 0x6b, 0x78, + 0x2e, 0xfd, 0xb7, 0x01, 0x8b, 0x05, 0x45, 0x9e, 0x3b, 0x28, 0x2b, 0x11, 0xd7, 0xa8, 0x89, 0xb8, + 0x1b, 0x30, 0x79, 0xe0, 0xb9, 0xfc, 0xf2, 0xe4, 0x76, 0x5d, 0x51, 0xe4, 0x34, 0x3d, 0x4c, 0x44, + 0xe1, 0xa8, 0x76, 0x72, 0x9c, 0x74, 0x27, 0x47, 0xa2, 0x72, 0x94, 0xca, 0x79, 0x98, 0xaa, 0x9e, + 0x07, 0xdd, 0x96, 0xd3, 0x65, 0x5b, 0xae, 0xe2, 0x05, 0x96, 0xd1, 0xce, 0x22, 0xcf, 0x01, 0xc8, + 0x81, 0x23, 0xdd, 0xfa, 0x6d, 0x80, 0x30, 0xc3, 0x94, 0xf1, 0xb7, 0x5e, 0x11, 0x3a, 0x0b, 0xc1, + 0x02, 0x32, 0x7d, 0x1f, 0xef, 0xc1, 0x22, 0x73, 0x69, 0xfc, 0xdb, 0x1a, 0x4d, 0x11, 0x8b, 0xa4, + 0x42, 0x33, 0xd1, 0x88, 0xbd, 0x8e, 0xc4, 0x76, 0x1d, 0x87, 0xbb, 0xbe, 0x50, 0x21, 0x8d, 0xbc, + 0x1f, 0x9f, 0xc2, 0x8c, 0xdc, 0x21, 0xc3, 0x42, 0x20, 0x34, 0x3c, 0x97, 0xbc, 0x05, 0x50, 0xb8, + 0x43, 0x84, 0x5e, 0x1b, 0x4a, 0x06, 0xb9, 0x49, 0x45, 0x03, 0xb2, 0x2b, 0xa0, 0xd3, 0x3e, 0x2c, + 0xd5, 0xa0, 0x70, 0x51, 0xb2, 0xfa, 0x46, 0x8a, 0xa2, 0xde, 0xc9, 0x65, 0x68, 0xa6, 0x61, 0x6a, + 0xfb, 0xd6, 0xa9, 0xed, 0x0f, 0x54, 0xc8, 0x02, 0x82, 0x9e, 0x72, 0x08, 0x26, 0xa8, 0xd0, 0x17, + 0x91, 0xcb, 0x13, 0x54, 0xe8, 0xbb, 0xd4, 0x86, 0xd5, 0xb2, 0xd2, 0xd2, 0x84, 0xa3, 0x5c, 0xf6, + 0x12, 0xcc, 0xda, 0x62, 0x8b, 0x52, 0xac, 0x5d, 0x52, 0xcc, 0xcc, 0x10, 0x28, 0xc1, 0x1b, 0x68, + 0x2f, 0x0c, 0xfa, 0xde, 0xa1, 0x8a, 0x8e, 0x17, 0x31, 0x59, 0x29, 0x58, 0x5e, 0x4f, 0xb8, 0x76, + 0x6a, 0x23, 0xb7, 0x96, 0x89, 0xcf, 0xf4, 0x27, 0x06, 0x74, 0xf6, 0xc3, 0x38, 0xed, 0x87, 0xbe, + 0x17, 0xee, 0xba, 0x6e, 0xcc, 0x92, 0x84, 0x97, 0x12, 0xb6, 0x78, 0x54, 0x45, 0x8e, 0x7c, 0xe5, + 0x19, 0xd2, 0x09, 0xbd, 0x40, 0xc4, 0x6a, 0x43, 0x1a, 0x28, 0xf4, 0x02, 0x1e, 0xaa, 0x64, 0x1b, + 0x9a, 0x2e, 0x4b, 0x9c, 0xd8, 0x8b, 0x78, 0x45, 0x2c, 0xd3, 0x42, 0x11, 0xc4, 0x09, 0x1f, 0xd8, + 0xbe, 0x1d, 0x38, 0x4c, 0x66, 0x76, 0xf5, 0x4a, 0x57, 0x30, 0x5d, 0x65, 0x92, 0x28, 0x3d, 0x3e, + 0xc4, 0xe8, 0x2f, 0x80, 0xa5, 0x2a, 0xdf, 0x84, 0xb9, 0x48, 0x01, 0x65, 0xf8, 0x75, 0x95, 0x85, + 0xca, 0xea, 0x98, 0x39, 0x2a, 0xdd, 0xe4, 0x85, 0x69, 0x4e, 0xef, 0xf1, 0xe0, 0xe4, 0xc4, 0x8e, + 0xcf, 0x15, 0xb7, 0x00, 0x26, 0xf7, 0x42, 0x2f, 0xe0, 0x86, 0xe2, 0x4a, 0xa9, 0xc2, 0x8b, 0x3f, + 0x17, 0x45, 0x6f, 0x68, 0xa2, 0x17, 0xad, 0x35, 0xa1, 0x5b, 0xeb, 0x12, 0x40, 0xc4, 0x62, 0x87, + 0x05, 0xa9, 0x7d, 0xa8, 0x34, 0x2e, 0x40, 0xe8, 0x11, 0x90, 0x47, 0xfd, 0xbe, 0xef, 0x05, 0x8c, + 0xb3, 0x95, 0xc2, 0x8c, 0xb0, 0xfe, 0x70, 0x19, 0x74, 0x4e, 0x13, 0x15, 0x4e, 0x1f, 0xc0, 0xe2, + 0xa3, 0xa0, 0x86, 0x91, 0x22, 0x67, 0x8c, 0x22, 0xd7, 0xa8, 0x90, 0x7b, 0x0f, 0x5a, 0x05, 0xc1, + 0x13, 0xf2, 0x06, 0xcc, 0x49, 0x19, 0x99, 0xca, 0x06, 0xbd, 0x2c, 0x1b, 0x54, 0x34, 0x34, 0x73, + 0x64, 0xfa, 0x4b, 0x03, 0x9a, 0xb9, 0x64, 0xbc, 0x47, 0x99, 0xe2, 0xe6, 0x56, 0x54, 0x2e, 0x65, + 0x54, 0x72, 0x9c, 0x5b, 0xf8, 0xf7, 0x5e, 0x90, 0xc6, 0xe7, 0xa6, 0x40, 0xee, 0x3d, 0x06, 0xc8, + 0x81, 0xfc, 0xc2, 0x3f, 0x66, 0xea, 0xfc, 0xf2, 0x47, 0xb2, 0x03, 0x53, 0xf9, 0xa1, 0x2d, 0x66, + 0xbf, 0xb2, 0x4d, 0x4c, 0x81, 0xf7, 0x66, 0xe3, 0x0d, 0x83, 0xfe, 0x71, 0x92, 0xf7, 0x22, 0x35, + 0xc1, 0x22, 0x63, 0xf0, 0x15, 0x68, 0x8a, 0xb3, 0xc0, 0x33, 0x80, 0x12, 0xb8, 0x95, 0xdd, 0x43, + 0xa1, 0x17, 0x98, 0x80, 0x67, 0x03, 0xd7, 0xc9, 0x6b, 0x30, 0x8f, 0xc2, 0x5a, 0xa1, 0x30, 0x88, + 0x3c, 0xd8, 0xfa, 0x86, 0x16, 0xa2, 0x48, 0x93, 0x91, 0x08, 0x56, 0xb4, 0x2d, 0x56, 0x22, 0x44, + 0x90, 0x97, 0xd4, 0xdb, 0x6a, 0xeb, 0x08, 0x29, 0x85, 0xb1, 0x24, 0x41, 0xb9, 0x26, 0x4c, 0xb7, + 0xe4, 0x54, 0x57, 0xc8, 0x0e, 0xb4, 0x24, 0x47, 0xb4, 0x8c, 0xbc, 0xe2, 0x74, 0x19, 0x9b, 0x62, + 0x23, 0x22, 0x90, 0x13, 0x58, 0x2e, 0x6e, 0xc8, 0x24, 0x9c, 0xc2, 0x8d, 0x6f, 0x8d, 0x2f, 0x61, + 0x50, 0x11, 0x90, 0x38, 0x95, 0x85, 0xde, 0x0f, 0xa0, 0x3b, 0x4c, 0xa1, 0x1a, 0xb7, 0xdf, 0xd4, + 0xdd, 0xbe, 0x5c, 0x13, 0x92, 0x49, 0xc1, 0xe3, 0xbd, 0x4f, 0x60, 0x6d, 0x88, 0x30, 0x35, 0xc4, + 0x6f, 0xe8, 0xc4, 0x97, 0x6a, 0x22, 0xb5, 0x18, 0x4d, 0x3f, 0x33, 0xa0, 0xb7, 0xeb, 0xba, 0x95, + 0xe4, 0x94, 0x77, 0xb0, 0x5f, 0x77, 0xca, 0xdd, 0x82, 0x8d, 0x5a, 0x81, 0x64, 0xab, 0xfd, 0x0c, + 0xb6, 0x4c, 0x76, 0x12, 0x9e, 0xb2, 0xaf, 0x5b, 0x64, 0xba, 0x0d, 0x97, 0x86, 0x71, 0x96, 0xb2, + 0xf5, 0xa0, 0xfb, 0x80, 0xe9, 0x73, 0x8a, 0xac, 0x30, 0xfa, 0x9b, 0x01, 0xf3, 0xfa, 0x04, 0xe3, + 0x7f, 0xd5, 0x47, 0xbf, 0x0c, 0x24, 0x66, 0x49, 0x6a, 0xc5, 0xa1, 0xef, 0xf3, 0x76, 0xda, 0x65, + 0xbe, 0x7d, 0x2e, 0x67, 0x27, 0x1d, 0xbe, 0x62, 0x8a, 0x85, 0xbb, 0x1c, 0x4e, 0xd6, 0x60, 0xc6, + 0x8e, 0x3c, 0x8b, 0x47, 0x8d, 0xe8, 0xa5, 0xa7, 0xed, 0xc8, 0x7b, 0x9f, 0x9d, 0x13, 0x0a, 0xf3, + 0x72, 0xc1, 0xf2, 0xd9, 0x29, 0xf3, 0xb1, 0xe6, 0x9b, 0x30, 0x9b, 0x62, 0xf9, 0xfb, 0x1c, 0xc4, + 0x7b, 0xdf, 0x28, 0xf6, 0x78, 0xf8, 0xe5, 0x43, 0x9a, 0x19, 0x94, 0xa6, 0x2d, 0xe1, 0x4a, 0x3b, + 0xfa, 0x29, 0xac, 0xd7, 0xd8, 0x42, 0xe6, 0xa8, 0x77, 0xa0, 0xad, 0x8f, 0x7a, 0x54, 0x9e, 0xca, + 0xaa, 0x56, 0x6d, 0xa3, 0xb9, 0xd0, 0xd7, 0xe8, 0xc8, 0xea, 0x13, 0x71, 0x4c, 0x3b, 0xcd, 0x06, + 0x2e, 0xf4, 0x33, 0x58, 0xce, 0x81, 0x7b, 0x61, 0x70, 0xca, 0xe2, 0x84, 0x47, 0x1b, 0x81, 0xc9, + 0x7e, 0x1c, 0x9e, 0x28, 0x53, 0xf3, 0x67, 0x5e, 0xb7, 0xa5, 0xa1, 0x0c, 0x83, 0x46, 0x1a, 0x72, + 0x9c, 0xd8, 0x4e, 0xd5, 0x2d, 0x85, 0xcf, 0xbc, 0x4e, 0xf6, 0x90, 0x08, 0xb3, 0x70, 0x4d, 0x84, + 0x6a, 0x53, 0xc2, 0x38, 0x17, 0xfa, 0x14, 0xcb, 0xc7, 0xa2, 0x28, 0x52, 0xc7, 0xef, 0x40, 0x53, + 0xe8, 0xc8, 0x77, 0x2a, 0xfd, 0x36, 0x35, 0xfd, 0x4a, 0x62, 0x9a, 0xd0, 0xcf, 0xa0, 0xf4, 0x1f, + 0x0d, 0x68, 0x61, 0xc5, 0x7a, 0x97, 0xa5, 0xb6, 0xe7, 0x8f, 0xae, 0xa5, 0x45, 0x0d, 0xda, 0xc8, + 0x6a, 0xd0, 0x17, 0x60, 0xbe, 0x38, 0xcc, 0x38, 0x57, 0xcd, 0x6c, 0x61, 0x94, 0x71, 0x4e, 0xae, + 0xc2, 0x02, 0xb6, 0xd6, 0x39, 0x96, 0x88, 0x99, 0x79, 0x84, 0x66, 0x68, 0x7a, 0x23, 0x30, 0x55, + 0x6a, 0x04, 0xf8, 0x32, 0x16, 0xd3, 0x56, 0xe2, 0xb9, 0x59, 0x9f, 0x80, 0x90, 0xc7, 0x9e, 0x5b, + 0x58, 0xc6, 0xdd, 0x33, 0x85, 0x65, 0xdc, 0xcd, 0x7b, 0xa0, 0x98, 0xe1, 0xa8, 0x12, 0x47, 0x3c, + 0xd8, 0x0e, 0x4f, 0x98, 0x2d, 0x05, 0x7c, 0xe2, 0x9d, 0xe0, 0x58, 0x32, 0x49, 0xed, 0x74, 0xa0, + 0xe6, 0x2c, 0xf2, 0x2d, 0x6f, 0xd3, 0xa0, 0xd8, 0xa6, 0xe5, 0x4d, 0x5d, 0x53, 0x6b, 0xea, 0x2e, + 0x43, 0x33, 0x8c, 0x58, 0x60, 0xc9, 0x16, 0xbb, 0x25, 0xaa, 0x07, 0x0e, 0x7a, 0x8a, 0x10, 0x39, + 0x32, 0x41, 0x9b, 0x27, 0xe3, 0xf4, 0xa5, 0xba, 0x61, 0x1a, 0x65, 0xc3, 0xa8, 0x46, 0x70, 0xe2, + 0xa2, 0x46, 0x90, 0xee, 0x62, 0x55, 0xac, 0x18, 0xcb, 0xf0, 0x79, 0x19, 0xa6, 0xd1, 0x4c, 0x2a, + 0x72, 0x96, 0xb5, 0x36, 0x46, 0x06, 0x85, 0x29, 0x71, 0xe8, 0x7b, 0x38, 0xcc, 0xc5, 0xa5, 0x71, + 0x44, 0x5f, 0x87, 0x59, 0xe1, 0x95, 0x2c, 0x6a, 0x66, 0xf0, 0xfd, 0xa1, 0x4b, 0xff, 0x6c, 0x00, + 0x79, 0x3c, 0x38, 0x38, 0xf1, 0xc6, 0xa7, 0x36, 0x7e, 0x83, 0x4e, 0x60, 0x12, 0xc3, 0x44, 0x84, + 0x23, 0x3e, 0x97, 0x22, 0x64, 0xb2, 0x1c, 0x21, 0xb9, 0x3b, 0xa7, 0xea, 0x7b, 0xf4, 0xe9, 0xa2, + 0xf3, 0x79, 0x8a, 0xf7, 0x3d, 0x16, 0xa4, 0x96, 0x1c, 0xb6, 0xf0, 0x14, 0x8f, 0x80, 0x87, 0x2e, + 0x7d, 0x0c, 0x4b, 0x9a, 0x66, 0xd2, 0xd2, 0x57, 0xa0, 0x25, 0x04, 0x88, 0x7c, 0xdb, 0xc9, 0x46, + 0xb5, 0x4d, 0x84, 0xed, 0x23, 0x68, 0x94, 0xbd, 0xfe, 0x6e, 0x00, 0xd9, 0xe3, 0x17, 0x97, 0x3f, + 0xb6, 0xbd, 0x78, 0xe0, 0x88, 0x2e, 0x29, 0xa7, 0x37, 0x27, 0x21, 0x0f, 0x75, 0x66, 0x13, 0x1a, + 0xb3, 0xcc, 0xd2, 0x93, 0xcf, 0x39, 0x0a, 0xa9, 0x9c, 0xda, 0xab, 0xb0, 0x70, 0x66, 0xfb, 0x3e, + 0x4b, 0x2d, 0x75, 0x57, 0xca, 0x99, 0xa9, 0x80, 0xaa, 0x8e, 0x4b, 0xf9, 0x6b, 0x26, 0xf7, 0x17, + 0x6f, 0x89, 0x34, 0x7d, 0xe5, 0xdd, 0x77, 0x07, 0x56, 0x05, 0x78, 0xd7, 0xf7, 0xc7, 0x3e, 0x43, + 0xf4, 0x57, 0x0d, 0x58, 0xab, 0x6c, 0xcb, 0x2e, 0x09, 0xfd, 0x04, 0x5c, 0xcb, 0xd4, 0xad, 0xdf, + 0x70, 0x4b, 0xbe, 0xca, 0x5d, 0xbd, 0xdf, 0x1b, 0x30, 0x2d, 0x40, 0x23, 0xbd, 0xf1, 0x89, 0x72, + 0xbf, 0xcc, 0x31, 0xa2, 0xfe, 0xfd, 0xd6, 0x78, 0xcc, 0xc4, 0xbf, 0xc7, 0xb8, 0x53, 0x94, 0x87, + 0x22, 0x6e, 0x04, 0xa4, 0xf7, 0x0e, 0x74, 0xca, 0x08, 0x35, 0x25, 0xdb, 0x72, 0xb1, 0x64, 0x9b, + 0x2b, 0x56, 0x67, 0xa2, 0x87, 0xbe, 0x77, 0xca, 0x82, 0x34, 0xbb, 0xe3, 0xbe, 0x34, 0xa0, 0xbd, + 0x17, 0x06, 0xae, 0xc7, 0xf3, 0xe3, 0xbe, 0x1d, 0xdb, 0x27, 0x09, 0xd9, 0xe4, 0x95, 0x8d, 0x04, + 0xa9, 0x21, 0x6b, 0x06, 0x18, 0x32, 0xce, 0xda, 0x02, 0x70, 0x8e, 0x98, 0x73, 0x6c, 0xc9, 0xf9, + 0x12, 0x0f, 0xfa, 0x39, 0x84, 0xbc, 0xeb, 0xb9, 0x09, 0x79, 0x05, 0x96, 0xf2, 0x65, 0xcb, 0x0e, + 0x5c, 0x4b, 0x0e, 0x97, 0x70, 0xde, 0x9c, 0xe1, 0xed, 0x06, 0xee, 0x6e, 0x72, 0x8c, 0x53, 0xf1, + 0x6c, 0xa6, 0x62, 0x69, 0x07, 0xb6, 0x9d, 0xc1, 0x77, 0x11, 0x4c, 0xff, 0x65, 0x60, 0xbe, 0x53, + 0x5a, 0x49, 0x6f, 0xe7, 0x63, 0x14, 0x9c, 0xae, 0x69, 0x2e, 0x6b, 0x94, 0x5c, 0x46, 0x60, 0xd2, + 0x4b, 0xd9, 0x89, 0x4a, 0x23, 0xfc, 0x99, 0xbc, 0x0b, 0x9d, 0x4c, 0x63, 0x2b, 0x42, 0xb3, 0xc8, + 0x63, 0xb2, 0x96, 0xb7, 0x09, 0x9a, 0xd5, 0xcc, 0xb6, 0x53, 0x32, 0xa3, 0x3a, 0x5e, 0x53, 0x17, + 0x1e, 0x2f, 0x9e, 0x95, 0x1c, 0xb4, 0xf6, 0xb4, 0x2c, 0xa2, 0xf0, 0x4d, 0x48, 0xcd, 0x9c, 0x41, + 0xca, 0x5c, 0x59, 0x18, 0x65, 0xef, 0xf4, 0xaf, 0x06, 0xb4, 0x77, 0x5d, 0x17, 0xf5, 0x1e, 0x27, + 0x4d, 0x28, 0x2d, 0x1b, 0x17, 0x68, 0x39, 0xf1, 0x5f, 0x6a, 0xf9, 0x95, 0x93, 0xc8, 0x10, 0x23, + 0x50, 0x0a, 0x9d, 0x5c, 0xcf, 0x7a, 0xf7, 0xd2, 0x6f, 0x00, 0x11, 0xc5, 0xb4, 0x66, 0x8e, 0x32, + 0xd6, 0x0a, 0x2c, 0x69, 0x58, 0x32, 0xd7, 0xdc, 0x87, 0xeb, 0x0f, 0x58, 0xba, 0x17, 0x9f, 0x47, + 0x69, 0xa8, 0x8a, 0x97, 0xbb, 0x2c, 0x0a, 0x13, 0x4f, 0x65, 0x2e, 0x36, 0x56, 0xf6, 0xf9, 0x83, + 0x01, 0x37, 0xc6, 0x20, 0x24, 0x55, 0xf8, 0x61, 0x75, 0x9a, 0xf0, 0xbd, 0x42, 0x23, 0x39, 0x1e, + 0x95, 0x5b, 0x19, 0x44, 0xa4, 0x8b, 0x9c, 0x64, 0xef, 0x6d, 0x58, 0xd0, 0x17, 0x9f, 0x2b, 0x55, + 0xf8, 0x70, 0xed, 0x02, 0x21, 0xc6, 0x89, 0xb9, 0x6b, 0xb0, 0xe0, 0x68, 0x24, 0x24, 0xa3, 0x12, + 0x94, 0xee, 0xc1, 0x8b, 0x17, 0x72, 0x93, 0x66, 0x1b, 0xda, 0x8f, 0xd1, 0xdf, 0x4e, 0xc2, 0xda, + 0xc7, 0x5e, 0x7a, 0xe4, 0xc6, 0xf6, 0x99, 0x8a, 0xbe, 0x71, 0x84, 0x2c, 0xb5, 0x6a, 0x8d, 0x6a, + 0x77, 0x79, 0x13, 0x16, 0xc3, 0x80, 0x61, 0x45, 0x69, 0x45, 0x76, 0x92, 0x9c, 0x85, 0xb1, 0xba, + 0x4b, 0xdb, 0x61, 0xc0, 0x78, 0x55, 0xb9, 0x2f, 0xc1, 0xa5, 0xdb, 0x78, 0xb2, 0x7c, 0x1b, 0x77, + 0x60, 0x22, 0xf2, 0x02, 0x39, 0x21, 0xe7, 0x8f, 0xfc, 0xee, 0x4c, 0x63, 0xdb, 0x2d, 0x50, 0x96, + 0x77, 0x27, 0x42, 0x33, 0xba, 0xc5, 0x99, 0xed, 0x4c, 0x69, 0x66, 0x5b, 0xb0, 0xc9, 0xac, 0xde, + 0xa3, 0x5e, 0x86, 0xa6, 0x7c, 0xb4, 0x52, 0xfb, 0x50, 0x16, 0xbc, 0x20, 0x41, 0x4f, 0xec, 0xc3, + 0x42, 0x3d, 0x04, 0x5a, 0x3d, 0xb4, 0x05, 0xd0, 0x67, 0xcc, 0xd2, 0x4a, 0xdf, 0xb9, 0x3e, 0x63, + 0x22, 0xe9, 0xf2, 0xc2, 0xe8, 0xc0, 0x0e, 0x8e, 0x2d, 0xec, 0x38, 0x5b, 0x42, 0x1c, 0x0e, 0xf8, + 0x90, 0x77, 0x9d, 0x57, 0xa0, 0x85, 0x8b, 0x4a, 0xa6, 0x79, 0x61, 0x51, 0x0e, 0xdb, 0xcd, 0x7b, + 0x67, 0x44, 0x71, 0xbc, 0xf4, 0xbc, 0xbb, 0x90, 0xef, 0xdf, 0xf3, 0xd2, 0xf3, 0x6c, 0x3f, 0xda, + 0x2c, 0x3e, 0xef, 0xb6, 0xf3, 0xfd, 0x7b, 0x02, 0xc4, 0xc5, 0x4b, 0xce, 0xbc, 0x3e, 0x13, 0xdf, + 0xa8, 0x3b, 0xc2, 0xca, 0x08, 0xd9, 0x0b, 0x5d, 0xec, 0x03, 0xce, 0xbc, 0xb8, 0xd0, 0x8a, 0x2c, + 0x8a, 0x86, 0x85, 0x03, 0xb3, 0xcf, 0xf7, 0x37, 0xa1, 0xa3, 0xc2, 0xa5, 0xf8, 0x93, 0x85, 0x98, + 0x25, 0x03, 0x3f, 0x55, 0x3f, 0x59, 0x10, 0x6f, 0xb7, 0x7f, 0xdd, 0x83, 0x85, 0x07, 0xa1, 0x08, + 0xd0, 0x27, 0xdc, 0x2f, 0x31, 0x79, 0x04, 0x33, 0xf2, 0x07, 0x0f, 0x64, 0xb5, 0x70, 0x6e, 0x0b, + 0x23, 0xff, 0xde, 0x5a, 0x05, 0x2e, 0x33, 0xce, 0xd2, 0x17, 0x7f, 0xfa, 0xcb, 0xcf, 0x1b, 0xf3, + 0xa4, 0xb9, 0x73, 0xfa, 0xda, 0xce, 0x21, 0x4b, 0x3d, 0x4e, 0xc5, 0x81, 0x56, 0xf1, 0x23, 0x3e, + 0xd9, 0x28, 0xec, 0x2e, 0xff, 0x16, 0xa0, 0xb7, 0x59, 0xbf, 0x28, 0xe9, 0x77, 0x91, 0x3e, 0x21, + 0x1d, 0x49, 0x3f, 0xfb, 0xe6, 0x4f, 0x3e, 0x87, 0x76, 0xe9, 0xfb, 0x3d, 0xa1, 0x39, 0xa9, 0x61, + 0x3f, 0x66, 0xe8, 0xbd, 0x30, 0x12, 0x47, 0x72, 0xbd, 0x84, 0x5c, 0xbb, 0x74, 0x89, 0x73, 0x75, + 0x05, 0x17, 0xc5, 0xf9, 0x4d, 0xe3, 0x26, 0x49, 0xb0, 0xab, 0x28, 0xfe, 0x08, 0x60, 0x2c, 0xde, + 0x97, 0x6b, 0x54, 0xd5, 0xac, 0xb9, 0x81, 0x7c, 0x57, 0xc8, 0x52, 0x49, 0x5b, 0xb4, 0x6a, 0x82, + 0x9f, 0x18, 0x0b, 0x3f, 0x72, 0xc0, 0x00, 0x19, 0x87, 0xef, 0x56, 0x0d, 0xdf, 0xfc, 0x47, 0x12, + 0xb4, 0x87, 0x5c, 0x97, 0x09, 0x29, 0x71, 0x0d, 0xd3, 0x88, 0x3c, 0x83, 0x85, 0x7b, 0xc1, 0xff, + 0xc7, 0xc8, 0x5b, 0xc8, 0x76, 0x8d, 0x22, 0x5b, 0x31, 0xcd, 0x29, 0xda, 0xf8, 0x63, 0x98, 0xcb, + 0xbe, 0xa8, 0x92, 0x6e, 0x41, 0x03, 0xed, 0xdb, 0x7d, 0x6f, 0xc8, 0x97, 0x59, 0x15, 0x38, 0x74, + 0x5e, 0x2a, 0x25, 0xbe, 0xb3, 0x72, 0xc2, 0x9f, 0x02, 0xe4, 0x9f, 0x6a, 0xc9, 0x7a, 0x85, 0x72, + 0x16, 0x99, 0xbd, 0xba, 0x25, 0x49, 0x7e, 0x15, 0xc9, 0x77, 0xc8, 0x82, 0x46, 0x3e, 0x91, 0xa1, + 0x9f, 0x7d, 0x51, 0xd3, 0x42, 0xbf, 0xfc, 0x71, 0xb7, 0x37, 0xfc, 0xab, 0x9e, 0x8a, 0x04, 0xaa, + 0xe2, 0x3e, 0xab, 0x15, 0xb9, 0x06, 0x87, 0x30, 0xaf, 0x7d, 0xe6, 0x23, 0x9b, 0x75, 0x5c, 0x92, + 0x3a, 0xf7, 0x57, 0xbf, 0x0d, 0xd2, 0x75, 0x64, 0xb5, 0x44, 0x16, 0xcb, 0xac, 0x12, 0x72, 0x8c, + 0xbf, 0x8e, 0x2a, 0x7c, 0x0d, 0x23, 0x45, 0x5a, 0xd5, 0x4f, 0x83, 0xbd, 0x4b, 0xc3, 0x96, 0x93, + 0xfa, 0x50, 0x93, 0xd7, 0x09, 0xc6, 0xb7, 0x70, 0xb8, 0xf8, 0x06, 0xa6, 0x39, 0x5c, 0xfb, 0x54, + 0xd6, 0x5b, 0xaf, 0x59, 0x91, 0xd4, 0x57, 0x90, 0x7a, 0x9b, 0x28, 0x9f, 0x3b, 0x82, 0x96, 0xf0, + 0x49, 0x36, 0x9c, 0xd4, 0x7c, 0x52, 0xfe, 0x82, 0xa5, 0xa5, 0xa3, 0xca, 0x77, 0xac, 0x4a, 0x3a, + 0xca, 0xbe, 0x54, 0x91, 0x1f, 0xeb, 0x1f, 0xc4, 0xd4, 0x80, 0x9e, 0x8e, 0x9c, 0xa8, 0x57, 0x4e, + 0xcb, 0xd0, 0xa9, 0x3b, 0xbd, 0x8c, 0x9c, 0xd7, 0xc9, 0x5a, 0x99, 0xb3, 0x9c, 0xe0, 0x93, 0x2f, + 0x0c, 0x58, 0xaa, 0x99, 0x0f, 0xe7, 0x12, 0x0c, 0x9f, 0x66, 0xe7, 0x12, 0x8c, 0x1a, 0x30, 0x53, + 0x94, 0x60, 0x93, 0xa2, 0x04, 0xb6, 0xeb, 0x66, 0x12, 0xc8, 0xdb, 0x91, 0x47, 0xe6, 0x4f, 0x0d, + 0x58, 0xad, 0x9f, 0x05, 0x93, 0xab, 0x8a, 0xc7, 0xc8, 0x29, 0x75, 0xef, 0xda, 0x45, 0x68, 0x52, + 0x9a, 0xab, 0x28, 0xcd, 0x65, 0xda, 0xe3, 0xd2, 0xc4, 0x88, 0x5b, 0x27, 0xd0, 0x19, 0xb6, 0x54, + 0xfa, 0xb4, 0x95, 0x6c, 0x17, 0x0c, 0x5e, 0x3b, 0x94, 0xee, 0x5d, 0x19, 0x81, 0xa1, 0xa7, 0x2f, + 0xb2, 0x22, 0x1d, 0x82, 0x23, 0xca, 0x6c, 0x6c, 0x2b, 0xcf, 0x68, 0x3e, 0xcd, 0xd4, 0xce, 0x68, + 0x65, 0x40, 0xab, 0x9d, 0xd1, 0xea, 0xcc, 0xb4, 0x72, 0x46, 0x91, 0x19, 0xce, 0x4f, 0xc9, 0x27, + 0x78, 0x6c, 0x64, 0x3f, 0xdf, 0x2d, 0x1f, 0xf5, 0xa4, 0xee, 0xd8, 0xe8, 0x1d, 0x7b, 0x25, 0x55, + 0x8a, 0x31, 0x01, 0xb7, 0x9e, 0x09, 0xb3, 0x0a, 0x9d, 0xac, 0x95, 0x09, 0x28, 0xca, 0xb5, 0x03, + 0x38, 0xba, 0x86, 0x44, 0x17, 0x69, 0xab, 0x48, 0x94, 0xd3, 0x3c, 0x80, 0x66, 0x61, 0xd8, 0x44, + 0xb2, 0x24, 0x5b, 0x9d, 0xad, 0xf5, 0x36, 0x6a, 0xd7, 0xf4, 0x54, 0x42, 0xdb, 0x9c, 0x41, 0x82, + 0x08, 0x45, 0x1e, 0x85, 0x51, 0x4c, 0xce, 0xa3, 0x3a, 0x8f, 0xca, 0x79, 0xd4, 0xcd, 0x6e, 0x34, + 0x1e, 0x0e, 0x22, 0x64, 0x3c, 0x62, 0x68, 0x97, 0x46, 0x20, 0xe4, 0xd2, 0xd0, 0xd9, 0x48, 0xe9, + 0xfe, 0x1f, 0x32, 0x3b, 0xd1, 0xeb, 0x0e, 0xc1, 0xcf, 0xf6, 0xfd, 0xdc, 0x1f, 0x22, 0x45, 0x8a, + 0x01, 0x81, 0xe6, 0x6b, 0x6d, 0x12, 0xa2, 0xf9, 0x5a, 0x9f, 0x26, 0x54, 0x52, 0x24, 0x13, 0xb4, + 0x9e, 0xc2, 0xac, 0xea, 0x4c, 0x73, 0x47, 0x97, 0x7a, 0xf2, 0x5e, 0xb7, 0xba, 0x20, 0xa9, 0x6a, + 0xce, 0xb6, 0x5d, 0x17, 0xa9, 0x4a, 0x47, 0x14, 0xfa, 0xd4, 0xdc, 0x11, 0xd5, 0x16, 0x37, 0x77, + 0x44, 0x5d, 0x63, 0xab, 0x39, 0x42, 0x9c, 0xf6, 0x8c, 0xc7, 0xef, 0x0c, 0xb8, 0x72, 0x61, 0x9b, + 0x49, 0x5e, 0x7d, 0x8e, 0x8e, 0x54, 0x08, 0xf4, 0xda, 0x73, 0xf7, 0xb0, 0xf4, 0x3a, 0x8a, 0x49, + 0xe9, 0x96, 0xba, 0x80, 0x70, 0x9b, 0x2b, 0xd0, 0xb3, 0x86, 0x96, 0x0b, 0xfd, 0x1b, 0x03, 0x2e, + 0x5f, 0x40, 0x97, 0xdc, 0x1a, 0x53, 0x00, 0x25, 0xf0, 0xce, 0xd8, 0xf8, 0x52, 0xdc, 0x6b, 0x28, + 0xee, 0x36, 0xdd, 0x18, 0x21, 0x2e, 0x17, 0xf6, 0x47, 0xb0, 0x91, 0xb5, 0xa3, 0x1a, 0xdd, 0xfb, + 0x83, 0xc0, 0x4d, 0x48, 0x16, 0xd6, 0x43, 0x7a, 0xd6, 0x3c, 0x70, 0xca, 0x5d, 0x8a, 0x7e, 0xa7, + 0x9c, 0xc9, 0x55, 0x21, 0x46, 0x9f, 0xd3, 0xe6, 0xdc, 0x23, 0x58, 0x54, 0xfb, 0xee, 0x7b, 0x76, + 0xfa, 0x95, 0x79, 0x6e, 0x23, 0xcf, 0x1e, 0x5d, 0x29, 0xf2, 0xec, 0x7b, 0x76, 0xaa, 0x38, 0x1e, + 0x4c, 0xe3, 0x6f, 0xc3, 0x5f, 0xff, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xa0, 0xe9, 0x5c, + 0x4e, 0x2e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4006,6 +4051,7 @@ type GoCryptoTraderClient interface { GetExchanges(ctx context.Context, in *GetExchangesRequest, opts ...grpc.CallOption) (*GetExchangesResponse, error) DisableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) GetExchangeInfo(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GetExchangeInfoResponse, error) + GetExchangeOTPCode(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GetExchangeOTPReponse, error) EnableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) GetTicker(ctx context.Context, in *GetTickerRequest, opts ...grpc.CallOption) (*TickerResponse, error) GetTickers(ctx context.Context, in *GetTickersRequest, opts ...grpc.CallOption) (*GetTickersResponse, error) @@ -4077,6 +4123,15 @@ func (c *goCryptoTraderClient) GetExchangeInfo(ctx context.Context, in *GenericE return out, nil } +func (c *goCryptoTraderClient) GetExchangeOTPCode(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GetExchangeOTPReponse, error) { + out := new(GetExchangeOTPReponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GetExchangeOTPCode", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *goCryptoTraderClient) EnableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) { out := new(GenericExchangeNameResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/EnableExchange", in, out, opts...) @@ -4308,6 +4363,7 @@ type GoCryptoTraderServer interface { GetExchanges(context.Context, *GetExchangesRequest) (*GetExchangesResponse, error) DisableExchange(context.Context, *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) GetExchangeInfo(context.Context, *GenericExchangeNameRequest) (*GetExchangeInfoResponse, error) + GetExchangeOTPCode(context.Context, *GenericExchangeNameRequest) (*GetExchangeOTPReponse, error) EnableExchange(context.Context, *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) GetTicker(context.Context, *GetTickerRequest) (*TickerResponse, error) GetTickers(context.Context, *GetTickersRequest) (*GetTickersResponse, error) @@ -4335,6 +4391,101 @@ type GoCryptoTraderServer interface { WithdrawFiatFunds(context.Context, *WithdrawCurrencyRequest) (*WithdrawResponse, error) } +// UnimplementedGoCryptoTraderServer can be embedded to have forward compatible implementations. +type UnimplementedGoCryptoTraderServer struct { +} + +func (*UnimplementedGoCryptoTraderServer) GetInfo(ctx context.Context, req *GetInfoRequest) (*GetInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetExchanges(ctx context.Context, req *GetExchangesRequest) (*GetExchangesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetExchanges not implemented") +} +func (*UnimplementedGoCryptoTraderServer) DisableExchange(ctx context.Context, req *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DisableExchange not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetExchangeInfo(ctx context.Context, req *GenericExchangeNameRequest) (*GetExchangeInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetExchangeInfo not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCode(ctx context.Context, req *GenericExchangeNameRequest) (*GetExchangeOTPReponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetExchangeOTPCode not implemented") +} +func (*UnimplementedGoCryptoTraderServer) EnableExchange(ctx context.Context, req *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EnableExchange not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetTicker(ctx context.Context, req *GetTickerRequest) (*TickerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTicker not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetTickers(ctx context.Context, req *GetTickersRequest) (*GetTickersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTickers not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetOrderbook(ctx context.Context, req *GetOrderbookRequest) (*OrderbookResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOrderbook not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetOrderbooks(ctx context.Context, req *GetOrderbooksRequest) (*GetOrderbooksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOrderbooks not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetAccountInfo(ctx context.Context, req *GetAccountInfoRequest) (*GetAccountInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAccountInfo not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetConfig(ctx context.Context, req *GetConfigRequest) (*GetConfigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetPortfolio(ctx context.Context, req *GetPortfolioRequest) (*GetPortfolioResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPortfolio not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetPortfolioSummary(ctx context.Context, req *GetPortfolioSummaryRequest) (*GetPortfolioSummaryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPortfolioSummary not implemented") +} +func (*UnimplementedGoCryptoTraderServer) AddPortfolioAddress(ctx context.Context, req *AddPortfolioAddressRequest) (*AddPortfolioAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddPortfolioAddress not implemented") +} +func (*UnimplementedGoCryptoTraderServer) RemovePortfolioAddress(ctx context.Context, req *RemovePortfolioAddressRequest) (*RemovePortfolioAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemovePortfolioAddress not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetForexProviders(ctx context.Context, req *GetForexProvidersRequest) (*GetForexProvidersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetForexProviders not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetForexRates(ctx context.Context, req *GetForexRatesRequest) (*GetForexRatesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetForexRates not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetOrders(ctx context.Context, req *GetOrdersRequest) (*GetOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOrders not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetOrder(ctx context.Context, req *GetOrderRequest) (*OrderDetails, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetOrder not implemented") +} +func (*UnimplementedGoCryptoTraderServer) SubmitOrder(ctx context.Context, req *SubmitOrderRequest) (*SubmitOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitOrder not implemented") +} +func (*UnimplementedGoCryptoTraderServer) CancelOrder(ctx context.Context, req *CancelOrderRequest) (*CancelOrderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelOrder not implemented") +} +func (*UnimplementedGoCryptoTraderServer) CancelAllOrders(ctx context.Context, req *CancelAllOrdersRequest) (*CancelAllOrdersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelAllOrders not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetEvents(ctx context.Context, req *GetEventsRequest) (*GetEventsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEvents not implemented") +} +func (*UnimplementedGoCryptoTraderServer) AddEvent(ctx context.Context, req *AddEventRequest) (*AddEventResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddEvent not implemented") +} +func (*UnimplementedGoCryptoTraderServer) RemoveEvent(ctx context.Context, req *RemoveEventRequest) (*RemoveEventResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveEvent not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddresses(ctx context.Context, req *GetCryptocurrencyDepositAddressesRequest) (*GetCryptocurrencyDepositAddressesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCryptocurrencyDepositAddresses not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddress(ctx context.Context, req *GetCryptocurrencyDepositAddressRequest) (*GetCryptocurrencyDepositAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCryptocurrencyDepositAddress not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WithdrawCryptocurrencyFunds(ctx context.Context, req *WithdrawCurrencyRequest) (*WithdrawResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawCryptocurrencyFunds not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WithdrawFiatFunds(ctx context.Context, req *WithdrawCurrencyRequest) (*WithdrawResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawFiatFunds not implemented") +} + func RegisterGoCryptoTraderServer(s *grpc.Server, srv GoCryptoTraderServer) { s.RegisterService(&_GoCryptoTrader_serviceDesc, srv) } @@ -4411,6 +4562,24 @@ func _GoCryptoTrader_GetExchangeInfo_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _GoCryptoTrader_GetExchangeOTPCode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenericExchangeNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).GetExchangeOTPCode(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/GetExchangeOTPCode", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).GetExchangeOTPCode(ctx, req.(*GenericExchangeNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _GoCryptoTrader_EnableExchange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GenericExchangeNameRequest) if err := dec(in); err != nil { @@ -4881,6 +5050,10 @@ var _GoCryptoTrader_serviceDesc = grpc.ServiceDesc{ MethodName: "GetExchangeInfo", Handler: _GoCryptoTrader_GetExchangeInfo_Handler, }, + { + MethodName: "GetExchangeOTPCode", + Handler: _GoCryptoTrader_GetExchangeOTPCode_Handler, + }, { MethodName: "EnableExchange", Handler: _GoCryptoTrader_EnableExchange_Handler, diff --git a/gctrpc/rpc.pb.gw.go b/gctrpc/rpc.pb.gw.go index 3da1471e..8619468a 100644 --- a/gctrpc/rpc.pb.gw.go +++ b/gctrpc/rpc.pb.gw.go @@ -9,13 +9,13 @@ It translates gRPC into RESTful JSON APIs. package gctrpc import ( + "context" "io" "net/http" "github.com/golang/protobuf/proto" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" @@ -45,7 +45,10 @@ func request_GoCryptoTrader_GetExchanges_0(ctx context.Context, marshaler runtim var protoReq GetExchangesRequest var metadata runtime.ServerMetadata - if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchanges_0); err != nil { + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchanges_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -79,7 +82,10 @@ func request_GoCryptoTrader_GetExchangeInfo_0(ctx context.Context, marshaler run var protoReq GenericExchangeNameRequest var metadata runtime.ServerMetadata - if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchangeInfo_0); err != nil { + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchangeInfo_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -88,6 +94,26 @@ func request_GoCryptoTrader_GetExchangeInfo_0(ctx context.Context, marshaler run } +var ( + filter_GoCryptoTrader_GetExchangeOTPCode_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_GetExchangeOTPCode_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GenericExchangeNameRequest + 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_GetExchangeOTPCode_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetExchangeOTPCode(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + func request_GoCryptoTrader_EnableExchange_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq GenericExchangeNameRequest var metadata runtime.ServerMetadata @@ -165,7 +191,10 @@ func request_GoCryptoTrader_GetAccountInfo_0(ctx context.Context, marshaler runt var protoReq GetAccountInfoRequest var metadata runtime.ServerMetadata - if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetAccountInfo_0); err != nil { + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetAccountInfo_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -567,6 +596,26 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_GetExchangeOTPCode_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) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_GetExchangeOTPCode_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_GetExchangeOTPCode_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_GoCryptoTrader_EnableExchange_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1079,6 +1128,8 @@ var ( pattern_GoCryptoTrader_GetExchangeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangeinfo"}, "")) + pattern_GoCryptoTrader_GetExchangeOTPCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangeotp"}, "")) + pattern_GoCryptoTrader_EnableExchange_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "enableexchange"}, "")) pattern_GoCryptoTrader_GetTicker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getticker"}, "")) @@ -1139,6 +1190,8 @@ var ( forward_GoCryptoTrader_GetExchangeInfo_0 = runtime.ForwardResponseMessage + forward_GoCryptoTrader_GetExchangeOTPCode_0 = runtime.ForwardResponseMessage + forward_GoCryptoTrader_EnableExchange_0 = runtime.ForwardResponseMessage forward_GoCryptoTrader_GetTicker_0 = runtime.ForwardResponseMessage diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 54030bf2..e7552126 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -28,6 +28,10 @@ message GetExchangesResponse { string exchanges = 1; } +message GetExchangeOTPReponse { + string otp_code = 1; +} + message DisableExchangeRequest { string exchange = 1; } @@ -412,6 +416,12 @@ service GoCryptoTrader { }; } + rpc GetExchangeOTPCode (GenericExchangeNameRequest) returns (GetExchangeOTPReponse) { + option (google.api.http) = { + get: "/v1/getexchangeotp" + }; + } + rpc EnableExchange (GenericExchangeNameRequest) returns (GenericExchangeNameResponse) { option (google.api.http) = { post: "/v1/enableexchange" diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 12564431..85c7f706 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -303,6 +303,30 @@ ] } }, + "/v1/getexchangeotp": { + "get": { + "operationId": "GetExchangeOTPCode", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetExchangeOTPReponse" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/getexchanges": { "get": { "operationId": "GetExchanges", @@ -1072,6 +1096,14 @@ } } }, + "gctrpcGetExchangeOTPReponse": { + "type": "object", + "properties": { + "otp_code": { + "type": "string" + } + } + }, "gctrpcGetExchangesResponse": { "type": "object", "properties": {