diff --git a/currency/pairs.go b/currency/pairs.go index cd4d55c9b97..b2a4d5d2052 100644 --- a/currency/pairs.go +++ b/currency/pairs.go @@ -210,12 +210,14 @@ func (p Pairs) Remove(pair Pair) (Pairs, error) { return nil, fmt.Errorf("%s %w", pair, ErrPairNotFound) } -// Add adds a specified pair to the list of pairs if it doesn't exist -func (p Pairs) Add(pair Pair) Pairs { - if p.Contains(pair, true) { - return p +// Add adds specified pairs to the list of pairs if they don't exist +func (p Pairs) Add(pairs ...Pair) Pairs { + for x := range pairs { + if p.Contains(pairs[x], true) { + continue + } + p = append(p, pairs[x]) } - p = append(p, pair) return p } diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index 7c599173997..c48d70c510f 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -151,7 +151,6 @@ var ( errNoWalletForCurrency = errors.New("no wallet found for currency, address creation impossible") errChannelNameUnknown = errors.New("unknown channel name") errNoWalletsReturned = errors.New("no wallets returned") - errUnknownEndpointLimit = errors.New("unknown endpoint limit") errPayMethodNotFound = errors.New("payment method not found") errUnknownL2DataType = errors.New("unknown l2update data type") errUnknownSide = errors.New("unknown side") diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index 164c5aa6442..2a02ae474a6 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -1675,7 +1675,7 @@ func TestProcessSnapshotUpdate(t *testing.T) { } func TestGenerateDefaultSubscriptions(t *testing.T) { - comparison := []subscription.Subscription{{Channel: "heartbeats"}, {Channel: "status"}, {Channel: "ticker"}, + comparison := subscription.List{{Channel: "heartbeats"}, {Channel: "status"}, {Channel: "ticker"}, {Channel: "ticker_batch"}, {Channel: "candles"}, {Channel: "market_trades"}, {Channel: "level2"}, {Channel: "user"}} for i := range comparison { diff --git a/exchanges/coinbasepro/coinbasepro_types.go b/exchanges/coinbasepro/coinbasepro_types.go index 27caa1fc9d3..836e32e6756 100644 --- a/exchanges/coinbasepro/coinbasepro_types.go +++ b/exchanges/coinbasepro/coinbasepro_types.go @@ -1473,417 +1473,3 @@ type AllWrappedAssets struct { type WrappedAssetConversionRate struct { Amount float64 `json:"amount,string"` } - -// // AccountHolds contains the hold information about an account -// type AccountHolds struct { -// ID string `json:"id"` -// AccountID string `json:"account_id"` -// CreatedAt time.Time `json:"created_at"` -// UpdatedAt string `json:"updated_at"` -// Amount float64 `json:"amount,string"` -// Type string `json:"type"` -// Reference string `json:"ref"` -// } - -// // GeneralizedOrderResponse is the generalized return type across order -// // placement and information collation -// type GeneralizedOrderResponse struct { -// ID string `json:"id"` -// Price float64 `json:"price,string"` -// Size float64 `json:"size,string"` -// ProductID string `json:"product_id"` -// Side string `json:"side"` -// Stp string `json:"stp"` -// Type string `json:"type"` -// TimeInForce string `json:"time_in_force"` -// PostOnly bool `json:"post_only"` -// CreatedAt time.Time `json:"created_at"` -// FillFees float64 `json:"fill_fees,string"` -// FilledSize float64 `json:"filled_size,string"` -// ExecutedValue float64 `json:"executed_value,string"` -// Status string `json:"status"` -// Settled bool `json:"settled"` -// Funds float64 `json:"funds,string"` -// SpecifiedFunds float64 `json:"specified_funds,string"` -// DoneReason string `json:"done_reason"` -// DoneAt time.Time `json:"done_at"` -// } - -// // Funding holds funding data -// type Funding struct { -// ID string `json:"id"` -// OrderID string `json:"order_id"` -// ProfileID string `json:"profile_id"` -// Amount float64 `json:"amount,string"` -// Status string `json:"status"` -// CreatedAt time.Time `json:"created_at"` -// Currency string `json:"currency"` -// RepaidAmount float64 `json:"repaid_amount"` -// DefaultAmount float64 `json:"default_amount,string"` -// RepaidDefault bool `json:"repaid_default"` -// } - -// // MarginTransfer holds margin transfer details -// type MarginTransfer struct { -// CreatedAt time.Time `json:"created_at"` -// ID string `json:"id"` -// UserID string `json:"user_id"` -// ProfileID string `json:"profile_id"` -// MarginProfileID string `json:"margin_profile_id"` -// Type string `json:"type"` -// Amount float64 `json:"amount,string"` -// Currency string `json:"currency"` -// AccountID string `json:"account_id"` -// MarginAccountID string `json:"margin_account_id"` -// MarginProductID string `json:"margin_product_id"` -// Status string `json:"status"` -// Nonce int `json:"nonce"` -// } - -// // AccountOverview holds account information returned from position -// type AccountOverview struct { -// Status string `json:"status"` -// Funding struct { -// MaxFundingValue float64 `json:"max_funding_value,string"` -// FundingValue float64 `json:"funding_value,string"` -// OldestOutstanding struct { -// ID string `json:"id"` -// OrderID string `json:"order_id"` -// CreatedAt time.Time `json:"created_at"` -// Currency string `json:"currency"` -// AccountID string `json:"account_id"` -// Amount float64 `json:"amount,string"` -// } `json:"oldest_outstanding"` -// } `json:"funding"` -// Accounts struct { -// LTC Account `json:"LTC"` -// ETH Account `json:"ETH"` -// USD Account `json:"USD"` -// BTC Account `json:"BTC"` -// } `json:"accounts"` -// MarginCall struct { -// Active bool `json:"active"` -// Price float64 `json:"price,string"` -// Side string `json:"side"` -// Size float64 `json:"size,string"` -// Funds float64 `json:"funds,string"` -// } `json:"margin_call"` -// UserID string `json:"user_id"` -// ProfileID string `json:"profile_id"` -// Position struct { -// Type string `json:"type"` -// Size float64 `json:"size,string"` -// Complement float64 `json:"complement,string"` -// MaxSize float64 `json:"max_size,string"` -// } `json:"position"` -// ProductID string `json:"product_id"` -// } - -// // Account is a sub-type for account overview -// type Account struct { -// ID string `json:"id"` -// Balance float64 `json:"balance,string"` -// Hold float64 `json:"hold,string"` -// FundedAmount float64 `json:"funded_amount,string"` -// DefaultAmount float64 `json:"default_amount,string"` -// } - -// // PaymentMethod holds payment method information -// type PaymentMethod struct { -// ID string `json:"id"` -// Type string `json:"type"` -// Name string `json:"name"` -// Currency string `json:"currency"` -// PrimaryBuy bool `json:"primary_buy"` -// PrimarySell bool `json:"primary_sell"` -// AllowBuy bool `json:"allow_buy"` -// AllowSell bool `json:"allow_sell"` -// AllowDeposits bool `json:"allow_deposits"` -// AllowWithdraw bool `json:"allow_withdraw"` -// Limits struct { -// Buy []LimitInfo `json:"buy"` -// InstantBuy []LimitInfo `json:"instant_buy"` -// Sell []LimitInfo `json:"sell"` -// Deposit []LimitInfo `json:"deposit"` -// } `json:"limits"` -// } - -// // LimitInfo is a sub-type for payment method -// type LimitInfo struct { -// PeriodInDays int `json:"period_in_days"` -// Total struct { -// Amount float64 `json:"amount,string"` -// Currency string `json:"currency"` -// } `json:"total"` -// } - -// // DepositWithdrawalInfo holds returned deposit information -// type DepositWithdrawalInfo struct { -// ID string `json:"id"` -// Amount float64 `json:"amount,string"` -// Currency string `json:"currency"` -// PayoutAt time.Time `json:"payout_at"` -// } - -// // CoinbaseAccounts holds coinbase account information -// type CoinbaseAccounts struct { -// ID string `json:"id"` -// Name string `json:"name"` -// Balance float64 `json:"balance,string"` -// Currency string `json:"currency"` -// Type string `json:"type"` -// Primary bool `json:"primary"` -// Active bool `json:"active"` -// WireDepositInformation struct { -// AccountNumber string `json:"account_number"` -// RoutingNumber string `json:"routing_number"` -// BankName string `json:"bank_name"` -// BankAddress string `json:"bank_address"` -// BankCountry struct { -// Code string `json:"code"` -// Name string `json:"name"` -// } `json:"bank_country"` -// AccountName string `json:"account_name"` -// AccountAddress string `json:"account_address"` -// Reference string `json:"reference"` -// } `json:"wire_deposit_information"` -// SepaDepositInformation struct { -// Iban string `json:"iban"` -// Swift string `json:"swift"` -// BankName string `json:"bank_name"` -// BankAddress string `json:"bank_address"` -// BankCountryName string `json:"bank_country_name"` -// AccountName string `json:"account_name"` -// AccountAddress string `json:"account_address"` -// Reference string `json:"reference"` -// } `json:"sep_deposit_information"` -// } - -// // Report holds historical information -// type Report struct { -// ID string `json:"id"` -// Type string `json:"type"` -// Status string `json:"status"` -// CreatedAt time.Time `json:"created_at"` -// CompletedAt time.Time `json:"completed_at"` -// ExpiresAt time.Time `json:"expires_at"` -// FileURL string `json:"file_url"` -// Params struct { -// StartDate time.Time `json:"start_date"` -// EndDate time.Time `json:"end_date"` -// } `json:"params"` -// } - -// // Volume type contains trailing volume information -// type Volume struct { -// ProductID string `json:"product_id"` -// ExchangeVolume float64 `json:"exchange_volume,string"` -// Volume float64 `json:"volume,string"` -// RecordedAt string `json:"recorded_at"` -// } - -// // OrderL1L2 is a type used in layer conversion -// type OrderL1L2 struct { -// Price float64 -// Amount float64 -// NumOrders float64 -// } - -// // OrderL3 is a type used in layer conversion -// type OrderL3 struct { -// Price float64 -// Amount float64 -// OrderID string -// } - -// // OrderbookL1L2 holds level 1 and 2 order book information -// type OrderbookL1L2 struct { -// Sequence int64 `json:"sequence"` -// Bids []OrderL1L2 `json:"bids"` -// Asks []OrderL1L2 `json:"asks"` -// } - -// // OrderbookL3 holds level 3 order book information -// type OrderbookL3 struct { -// Sequence int64 `json:"sequence"` -// Bids []OrderL3 `json:"bids"` -// Asks []OrderL3 `json:"asks"` -// } - -// // OrderbookResponse is a generalized response for order books -// type OrderbookResponse struct { -// Sequence int64 `json:"sequence"` -// Bids [][3]interface{} `json:"bids"` -// Asks [][3]interface{} `json:"asks"` -// } - -// // FillResponse contains fill information from the exchange -// type FillResponse struct { -// TradeID int64 `json:"trade_id"` -// ProductID string `json:"product_id"` -// Price float64 `json:"price,string"` -// Size float64 `json:"size,string"` -// OrderID string `json:"order_id"` -// CreatedAt time.Time `json:"created_at"` -// Liquidity string `json:"liquidity"` -// Fee float64 `json:"fee,string"` -// Settled bool `json:"settled"` -// Side string `json:"side"` -// } - -// // WebsocketSubscribe takes in subscription information -// type WebsocketSubscribe struct { -// Type string `json:"type"` -// ProductIDs []string `json:"product_ids,omitempty"` -// Channels []any `json:"channels,omitempty"` -// Signature string `json:"signature,omitempty"` -// Key string `json:"key,omitempty"` -// Passphrase string `json:"passphrase,omitempty"` -// Timestamp string `json:"timestamp,omitempty"` -// } - -// // WsChannel defines a websocket subscription channel -// type WsChannel struct { -// Name string `json:"name"` -// ProductIDs []string `json:"product_ids,omitempty"` -// } - -// // wsOrderReceived holds websocket received values -// type wsOrderReceived struct { -// Type string `json:"type"` -// OrderID string `json:"order_id"` -// OrderType string `json:"order_type"` -// Size float64 `json:"size,string"` -// Price float64 `json:"price,omitempty,string"` -// Funds float64 `json:"funds,omitempty,string"` -// Side string `json:"side"` -// ClientOID string `json:"client_oid"` -// ProductID string `json:"product_id"` -// Sequence int64 `json:"sequence"` -// Time time.Time `json:"time"` -// RemainingSize float64 `json:"remaining_size,string"` -// NewSize float64 `json:"new_size,string"` -// OldSize float64 `json:"old_size,string"` -// Reason string `json:"reason"` -// Timestamp float64 `json:"timestamp,string"` -// UserID string `json:"user_id"` -// ProfileID string `json:"profile_id"` -// StopType string `json:"stop_type"` -// StopPrice float64 `json:"stop_price,string"` -// TakerFeeRate float64 `json:"taker_fee_rate,string"` -// Private bool `json:"private"` -// TradeID int64 `json:"trade_id"` -// MakerOrderID string `json:"maker_order_id"` -// TakerOrderID string `json:"taker_order_id"` -// TakerUserID string `json:"taker_user_id"` -// } - -// // WebsocketHeartBeat defines JSON response for a heart beat message -// type WebsocketHeartBeat struct { -// Type string `json:"type"` -// Sequence int64 `json:"sequence"` -// LastTradeID int64 `json:"last_trade_id"` -// ProductID string `json:"product_id"` -// Time string `json:"time"` -// } - -// // WebsocketTicker defines ticker websocket response -// type WebsocketTicker struct { -// Type string `json:"type"` -// Sequence int64 `json:"sequence"` -// ProductID currency.Pair `json:"product_id"` -// Price float64 `json:"price,string"` -// Open24H float64 `json:"open_24h,string"` -// Volume24H float64 `json:"volume_24h,string"` -// Low24H float64 `json:"low_24h,string"` -// High24H float64 `json:"high_24h,string"` -// Volume30D float64 `json:"volume_30d,string"` -// BestBid float64 `json:"best_bid,string"` -// BestAsk float64 `json:"best_ask,string"` -// Side string `json:"side"` -// Time time.Time `json:"time"` -// TradeID int64 `json:"trade_id"` -// LastSize float64 `json:"last_size,string"` -// } - -// // WebsocketOrderbookSnapshot defines a snapshot response -// type WebsocketOrderbookSnapshot struct { -// ProductID string `json:"product_id"` -// Type string `json:"type"` -// Bids [][2]string `json:"bids"` -// Asks [][2]string `json:"asks"` -// Time time.Time `json:"time"` -// } - -// // WebsocketL2Update defines an update on the L2 orderbooks -// type WebsocketL2Update struct { -// Type string `json:"type"` -// ProductID string `json:"product_id"` -// Time time.Time `json:"time"` -// Changes [][3]string `json:"changes"` -// } - -// type wsMsgType struct { -// Type string `json:"type"` -// Sequence int64 `json:"sequence"` -// ProductID string `json:"product_id"` -// } - -// type wsStatus struct { -// Currencies []struct { -// ConvertibleTo []string `json:"convertible_to"` -// Details struct{} `json:"details"` -// ID string `json:"id"` -// MaxPrecision float64 `json:"max_precision,string"` -// MinSize float64 `json:"min_size,string"` -// Name string `json:"name"` -// Status string `json:"status"` -// StatusMessage interface{} `json:"status_message"` -// } `json:"currencies"` -// Products []struct { -// BaseCurrency string `json:"base_currency"` -// BaseIncrement float64 `json:"base_increment,string"` -// BaseMaxSize float64 `json:"base_max_size,string"` -// BaseMinSize float64 `json:"base_min_size,string"` -// CancelOnly bool `json:"cancel_only"` -// DisplayName string `json:"display_name"` -// ID string `json:"id"` -// LimitOnly bool `json:"limit_only"` -// MaxMarketFunds float64 `json:"max_market_funds,string"` -// MinMarketFunds float64 `json:"min_market_funds,string"` -// PostOnly bool `json:"post_only"` -// QuoteCurrency string `json:"quote_currency"` -// QuoteIncrement float64 `json:"quote_increment,string"` -// Status string `json:"status"` -// StatusMessage interface{} `json:"status_message"` -// } `json:"products"` -// Type string `json:"type"` -// } - -// // RequestParamsTimeForceType Time in force -// type RequestParamsTimeForceType string - -// var ( -// // CoinbaseRequestParamsTimeGTC GTC -// CoinbaseRequestParamsTimeGTC = RequestParamsTimeForceType("GTC") - -// // CoinbaseRequestParamsTimeIOC IOC -// CoinbaseRequestParamsTimeIOC = RequestParamsTimeForceType("IOC") -// ) - -// // TransferHistory returns wallet transfer history -// type TransferHistory struct { -// ID string `json:"id"` -// Type string `json:"type"` -// CreatedAt string `json:"created_at"` -// CompletedAt string `json:"completed_at"` -// CanceledAt time.Time `json:"canceled_at"` -// ProcessedAt time.Time `json:"processed_at"` -// UserNonce int64 `json:"user_nonce"` -// Amount string `json:"amount"` -// Details struct { -// CoinbaseAccountID string `json:"coinbase_account_id"` -// CoinbaseTransactionID string `json:"coinbase_transaction_id"` -// CoinbasePaymentMethodID string `json:"coinbase_payment_method_id"` -// } `json:"details"` -// } diff --git a/exchanges/coinbasepro/coinbasepro_websocket.go b/exchanges/coinbasepro/coinbasepro_websocket.go index a0cb70a23ee..7334178aaf4 100644 --- a/exchanges/coinbasepro/coinbasepro_websocket.go +++ b/exchanges/coinbasepro/coinbasepro_websocket.go @@ -326,31 +326,6 @@ func (c *CoinbasePro) generateSubscriptions() (subscription.List, error) { return subscriptions, nil } -// // generateSubscriptions returns a list of subscriptions from the configured subscriptions feature -// func (c *CoinbasePro) generateSubscriptions() (subscription.List, error) { -// pairs, err := c.GetEnabledPairs(asset.Spot) -// if err != nil { -// return nil, err -// } -// pairFmt, err := c.GetPairFormat(asset.Spot, true) -// if err != nil { -// return nil, err -// } -// pairs = pairs.Format(pairFmt) -// authed := c.IsWebsocketAuthenticationSupported() -// subs := make(subscription.List, 0, len(c.Features.Subscriptions)) -// for _, baseSub := range c.Features.Subscriptions { -// if !authed && baseSub.Authenticated { -// continue -// } - -// s := baseSub.Clone() -// s.Asset = asset.Spot -// s.Pairs = pairs -// subs = append(subs, s) -// } -// } - // Subscribe sends a websocket message to receive data from the channel func (c *CoinbasePro) Subscribe(channelsToSubscribe subscription.List) error { chanKeys := make(map[string]currency.Pairs) @@ -368,63 +343,6 @@ func (c *CoinbasePro) Subscribe(channelsToSubscribe subscription.List) error { return nil } -// func (c *CoinbasePro) Subscribe(subs subscription.List) error { -// r := &WebsocketSubscribe{ -// Type: "subscribe", -// Channels: make([]any, 0, len(subs)), -// } -// // See if we have a consistent Pair list for all the subs that we can use globally -// // If all the subs have the same pairs then we can use the top level ProductIDs field -// // Otherwise each and every sub needs to have it's own list -// for i, s := range subs { -// if i == 0 { -// r.ProductIDs = s.Pairs.Strings() -// } else if !subs[0].Pairs.Equal(s.Pairs) { -// r.ProductIDs = nil -// break -// } -// } -// for _, s := range subs { -// if s.Authenticated && r.Key == "" && c.IsWebsocketAuthenticationSupported() { -// if err := c.authWsSubscibeReq(r); err != nil { -// return err -// } -// } -// if len(r.ProductIDs) == 0 { -// r.Channels = append(r.Channels, WsChannel{ -// Name: s.Channel, -// ProductIDs: s.Pairs.Strings(), -// }) -// } else { -// // Coinbase does not support using [WsChannel{Name:"x"}] unless each ProductIDs field is populated -// // Therefore we have to use Channels as an array of strings -// r.Channels = append(r.Channels, s.Channel) -// } -// } -// err := c.Websocket.Conn.SendJSONMessage(r) -// if err == nil { -// err = c.Websocket.AddSuccessfulSubscriptions(subs...) -// } -// return err -// } - -// func (c *CoinbasePro) authWsSubscibeReq(r *WebsocketSubscribe) error { -// creds, err := c.GetCredentials(context.TODO()) -// if err != nil { -// return err -// } -// r.Timestamp = strconv.FormatInt(time.Now().Unix(), 10) -// message := r.Timestamp + http.MethodGet + "/users/self/verify" -// hmac, err := crypto.GetHMAC(crypto.HashSHA256, []byte(message), []byte(creds.Secret)) -// if err != nil { -// return err -// } -// r.Signature = crypto.Base64Encode(hmac) -// r.Key = creds.Key -// r.Passphrase = creds.ClientID -// return nil -// } - // Unsubscribe sends a websocket message to stop receiving data from the channel func (c *CoinbasePro) Unsubscribe(channelsToUnsubscribe subscription.List) error { chanKeys := make(map[string]currency.Pairs) @@ -442,24 +360,6 @@ func (c *CoinbasePro) Unsubscribe(channelsToUnsubscribe subscription.List) error return nil } -// func (c *CoinbasePro) Unsubscribe(subs subscription.List) error { -// r := &WebsocketSubscribe{ -// Type: "unsubscribe", -// Channels: make([]any, 0, len(subs)), -// } -// for _, s := range subs { -// r.Channels = append(r.Channels, WsChannel{ -// Name: s.Channel, -// ProductIDs: s.Pairs.Strings(), -// }) -// } -// err := c.Websocket.Conn.SendJSONMessage(r) -// if err == nil { -// err = c.Websocket.RemoveSubscriptions(subs...) -// } -// return err -// } - // GetJWT checks if the current JWT is valid, returns it if it is, generates a new one if it isn't // Also suitable for use in REST requests, by checking for the presence of a URI, and always generating // a new JWT if one is not provided